diff --git a/.github/workflows/dev_api_ci_cd.yml b/.github/workflows/dev_api_ci_cd.yml index 1287c694..d42071ac 100644 --- a/.github/workflows/dev_api_ci_cd.yml +++ b/.github/workflows/dev_api_ci_cd.yml @@ -3,8 +3,9 @@ name: dev MATCH API Server CI/CD on: push: branches: [dev] - #tags: - # - Dev-Api-v*.*.* + pull_request: + branches: [dev] + types: [closed] workflow_dispatch: # 수동 실행 옵션 (생략) jobs: @@ -60,14 +61,18 @@ jobs: portone.secret : ${{ secrets.PORTONE_SECRET }} portone.billmid: ${{ secrets.DEV_PORTONE_BILL_MID}} + - name: Set Environment Domain - Domain DEV Yml uses: microsoft/variable-substitution@v1 with: files: ./Match-Domain/src/main/resources/application-domain-dev.yml env: - spring.datasource.url: ${{ secrets.DB_URL_HOST }} - spring.datasource.username: ${{ secrets.AWS_DB_USER_NAME }} - spring.datasource.password: ${{ secrets.AWS_DB_PASSWORD }} + spring.datasource.master.hikari.jdbc-url: ${{ secrets.DB_URL_HOST }} + spring.datasource.master.hikari.username: ${{ secrets.AWS_DB_USER_NAME }} + spring.datasource.master.hikari.password: ${{ secrets.AWS_DB_PASSWORD }} + spring.datasource.slave.hikari.jdbc-url: ${{ secrets.DEV_SLAVE_URL_HOST }} + spring.datasource.slave.hikari.username: ${{ secrets.AWS_DB_USER_NAME }} + spring.datasource.slave.hikari.password: ${{ secrets.AWS_DB_PASSWORD }} spring.redis.host : ${{ secrets.REDIS_DEV_HOST }} - name: create-fcm-json diff --git a/.github/workflows/prod_api_ci_cd.yml b/.github/workflows/prod_api_ci_cd.yml index 466dbf34..d0000d30 100644 --- a/.github/workflows/prod_api_ci_cd.yml +++ b/.github/workflows/prod_api_ci_cd.yml @@ -4,14 +4,15 @@ on: push: tags: - Api-Release-v*.*.* - #tags: - # - Dev-Api-v*.*.* workflow_dispatch: # 수동 실행 옵션 (생략) jobs: build: runs-on: ubuntu-latest # action 스크립트가 작동될 OS + outputs: + version: ${{ steps.get_version.outputs.BRANCH_NAME }} + steps: # 작업 단계 - name: Checkout source code # 단계별 이름, 구분자로 소스를 가져옴 uses: actions/checkout@v2 @@ -70,9 +71,12 @@ jobs: with: files: ./Match-Domain/src/main/resources/application-domain-prod.yml env: - spring.datasource.url: ${{ secrets.PROD_DB_URL_HOST }} - spring.datasource.username: ${{ secrets.AWS_DB_USER_NAME }} - spring.datasource.password: ${{ secrets.AWS_DB_PASSWORD }} + spring.datasource.master.hikari.jdbc-url: ${{ secrets.PROD_DB_URL_HOST }} + spring.datasource.master.hikari.username: ${{ secrets.AWS_DB_USER_NAME }} + spring.datasource.master.hikari.password: ${{ secrets.AWS_DB_PASSWORD }} + spring.datasource.slave.hikari.jdbc-url: ${{ secrets.PROD_SLAVE_URL_HOST }} + spring.datasource.slave.hikari.username: ${{ secrets.AWS_DB_USER_NAME }} + spring.datasource.slave.hikari.password: ${{ secrets.AWS_DB_PASSWORD }} spring.redis.host : ${{ secrets.REDIS_PROD_HOST }} - name: create-fcm-json diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/banner/controller/AdminBannerController.java b/Match-Api/src/main/java/com/example/matchapi/admin/banner/controller/AdminBannerController.java new file mode 100644 index 00000000..1cb03655 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/banner/controller/AdminBannerController.java @@ -0,0 +1,34 @@ +package com.example.matchapi.admin.banner.controller; + +import com.example.matchapi.admin.banner.service.AdminBannerService; +import com.example.matchapi.banner.dto.BannerReq; +import com.example.matchapi.banner.dto.BannerRes; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchdomain.banner.enums.BannerType; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@Slf4j +@RequestMapping("/admin/banners") +@Tag(name = "ADMIN-08-Banner💳 관리자 배너 관리 API") +public class AdminBannerController { + private final AdminBannerService adminBannerService; + @PostMapping("") + @Operation(summary = "ADMIN-08-01 배너 업로드") + public CommonResponse> uploadBanner( + @RequestParam BannerType bannerType, + @RequestPart MultipartFile bannerImage, + @RequestPart BannerReq.BannerUpload bannerUploadDto + ){ + System.out.println(bannerUploadDto.getContentsUrl()); + return CommonResponse.onSuccess(adminBannerService.uploadBanner(bannerType, bannerImage, bannerUploadDto)); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/banner/service/AdminBannerService.java b/Match-Api/src/main/java/com/example/matchapi/admin/banner/service/AdminBannerService.java new file mode 100644 index 00000000..600c7307 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/banner/service/AdminBannerService.java @@ -0,0 +1,44 @@ +package com.example.matchapi.admin.banner.service; + +import com.example.matchapi.banner.convertor.BannerConvertor; +import com.example.matchapi.banner.dto.BannerReq; +import com.example.matchapi.banner.dto.BannerRes; +import com.example.matchdomain.banner.adaptor.BannerAdaptor; +import com.example.matchdomain.banner.entity.Banner; +import com.example.matchdomain.banner.enums.BannerType; +import com.example.matchdomain.banner.repository.BannerRepository; +import com.example.matchinfrastructure.config.s3.S3UploadService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.transaction.Transactional; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class AdminBannerService { + private final BannerRepository bannerRepository; + private final BannerConvertor bannerConvertor; + private final S3UploadService s3UploadService; + private final BannerAdaptor bannerAdaptor; + + @Transactional + @CachePut(cacheNames = "bannerCache", key = "'all'") + public List uploadBanner(BannerType bannerType, + MultipartFile bannerImage, + BannerReq.BannerUpload bannerUploadDto) { + String bannerImg = s3UploadService.uploadBannerImage(bannerImage); + bannerRepository.save(bannerConvertor.convertToBannerUpload(bannerType, bannerImg, bannerUploadDto)); + return cachingBannerList(); + } + + @CacheEvict(cacheNames = "bannerCache") + public List cachingBannerList() { + List banners = bannerAdaptor.getBannerList(); + + return bannerConvertor.convertToBannerList(banners); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/donation/convertor/AdminDonationConvertor.java b/Match-Api/src/main/java/com/example/matchapi/admin/donation/convertor/AdminDonationConvertor.java index 8817c5aa..eb5dd3a2 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/donation/convertor/AdminDonationConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/donation/convertor/AdminDonationConvertor.java @@ -8,6 +8,8 @@ import com.example.matchdomain.donation.entity.HistoryImage; import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import java.util.List; + @Convertor public class AdminDonationConvertor { public DonationRes.DonationDetail getDonationDetail(DonationUser donationUser) { @@ -28,15 +30,16 @@ public DonationRes.DonationDetail getDonationDetail(DonationUser donationUser) { .build(); } - public DonationHistory DonationHistoryComplete(Long projectId) { + public DonationHistory convertToDonationHistoryComplete(Long projectId, List donationUserLists) { return DonationHistory .builder() .projectId(projectId) .historyStatus(HistoryStatus.COMPLETE) + .completeIdLists(donationUserLists) .build(); } - public HistoryImage HistoryImage(String image, Long id) { + public HistoryImage convertToHistoryImage(String image, Long id) { return HistoryImage .builder() .imgUrl(image) @@ -44,12 +47,13 @@ public HistoryImage HistoryImage(String image, Long id) { .build(); } - public DonationHistory DonationHistoryChange(DonationReq.EnforceDonation enforceDonation) { + public DonationHistory convertToDonationHistoryChange(DonationReq.EnforceDonation enforceDonation) { return DonationHistory .builder() .projectId(enforceDonation.getProjectId()) .cnt(enforceDonation.getDonationUserLists().size()) .historyStatus(HistoryStatus.CHANGE) + .changeIdLists(enforceDonation.getDonationUserLists()) .build(); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java b/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java index 70e7fa74..69349589 100644 --- a/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java +++ b/Match-Api/src/main/java/com/example/matchapi/admin/donation/service/AdminDonationService.java @@ -72,9 +72,9 @@ public DonationRes.DonationDetail getDonationDetail(Long donationId) { @Transactional public void enforceDonation(List imageLists, DonationReq.EnforceDonation enforceDonation) { - donationHistoryRepository.save(adminDonationConvertor.DonationHistoryChange(enforceDonation)); + donationHistoryRepository.save(adminDonationConvertor.convertToDonationHistoryChange(enforceDonation)); - DonationHistory donationHistory = donationHistoryRepository.save(adminDonationConvertor.DonationHistoryComplete(enforceDonation.getProjectId())); + DonationHistory donationHistory = donationHistoryRepository.save(adminDonationConvertor.convertToDonationHistoryComplete(enforceDonation.getProjectId(), enforceDonation.getDonationUserLists())); saveDonationHistoryImages(imageLists, donationHistory.getId()); @@ -96,7 +96,7 @@ private void saveDonationHistoryImages(List imageLists, Long hist List historyImages = new ArrayList<>(); for(String image : images){ - historyImages.add(adminDonationConvertor.HistoryImage(image, historyId)); + historyImages.add(adminDonationConvertor.convertToHistoryImage(image, historyId)); } historyImageRepository.saveAll(historyImages); } diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/event/controller/AdminEventController.java b/Match-Api/src/main/java/com/example/matchapi/admin/event/controller/AdminEventController.java new file mode 100644 index 00000000..e7eb9a14 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/event/controller/AdminEventController.java @@ -0,0 +1,31 @@ +package com.example.matchapi.admin.event.controller; + +import com.example.matchapi.admin.event.dto.EventUploadReq; +import com.example.matchapi.admin.event.service.AdminEventService; +import com.example.matchapi.event.dto.EventRes; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchinfrastructure.config.s3.S3UploadService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/events") +@Tag(name = "ADMIN-09-Event🎉 관리자 이벤트 관리 API") +public class AdminEventController { + private final AdminEventService adminEventService; + + @PostMapping("") + @Operation(summary = "ADMIN-09-01 이벤트 업로드") + public CommonResponse uploadEventList(@RequestBody EventUploadReq eventUploadReq){ + adminEventService.uploadEventList(eventUploadReq); + return CommonResponse.onSuccess("업로드 성공"); + } + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/event/dto/EventUploadReq.java b/Match-Api/src/main/java/com/example/matchapi/admin/event/dto/EventUploadReq.java new file mode 100644 index 00000000..fcd22bdf --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/event/dto/EventUploadReq.java @@ -0,0 +1,34 @@ +package com.example.matchapi.admin.event.dto; + +import com.example.matchapi.common.model.ContentsList; +import com.example.matchdomain.common.model.ContentsType; +import com.example.matchdomain.event.enums.EventType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotBlank; +import java.time.LocalDate; +import java.util.List; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EventUploadReq { + private String title; + + private String smallTitle; + + private String thumbnail; + + private EventType eventType; + + private LocalDate eventStartDate; + + private LocalDate eventEndDate; + + private List contentsList; + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/event/service/AdminEventService.java b/Match-Api/src/main/java/com/example/matchapi/admin/event/service/AdminEventService.java new file mode 100644 index 00000000..c914f324 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/event/service/AdminEventService.java @@ -0,0 +1,57 @@ +package com.example.matchapi.admin.event.service; + +import com.example.matchapi.admin.event.dto.EventUploadReq; +import com.example.matchapi.common.model.ContentsList; +import com.example.matchapi.event.convetor.EventConvertor; +import com.example.matchapi.event.dto.EventRes; +import com.example.matchapi.event.service.EventService; +import com.example.matchcommon.annotation.RedissonLock; +import com.example.matchdomain.common.model.ContentsType; +import com.example.matchdomain.event.entity.Event; +import com.example.matchdomain.event.entity.EventContent; +import com.example.matchdomain.event.repository.EventContentRepository; +import com.example.matchdomain.event.repository.EventRepository; +import com.example.matchinfrastructure.config.s3.S3UploadService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +import static com.example.matchcommon.constants.MatchStatic.EVENT_S3_DIR; +import static com.example.matchdomain.common.model.ContentsType.IMG; +import static com.example.matchdomain.common.model.ContentsType.TEXT; + +@Service +@RequiredArgsConstructor +public class AdminEventService { + private final EventRepository eventRepository; + private final EventContentRepository eventContentRepository; + private final EventConvertor eventConvertor; + private final EventService eventService; + @RedissonLock(LockName = "이벤트 업로드", key = "#eventUploadReq") + @CacheEvict(value = "eventCache", allEntries = true) + public void uploadEventList(EventUploadReq eventUploadReq) { + + Event event = eventRepository.save(eventConvertor.convertToEventUpload(eventUploadReq, eventUploadReq.getThumbnail())); + + Long eventId = event.getId(); + + List eventContents = new ArrayList<>(); + for(ContentsList content : eventUploadReq.getContentsList()){ + if(content.getContentsType().equals(IMG)){ + eventContents.add(eventConvertor.convertToEventContents(eventId, content.getContents(), IMG)); + } + else{ + eventContents.add(eventConvertor.convertToEventContents(eventId, content.getContents(), TEXT)); + } + } + + eventContentRepository.saveAll(eventContents); + } + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/controller/AdminKeywordController.java b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/controller/AdminKeywordController.java new file mode 100644 index 00000000..b233bf8e --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/controller/AdminKeywordController.java @@ -0,0 +1,37 @@ +package com.example.matchapi.admin.keyword.controller; + +import com.example.matchapi.admin.keyword.dto.AdminKeywordReq; +import com.example.matchapi.admin.keyword.service.AdminKeywordService; +import com.example.matchapi.keword.dto.KeywordRes; +import com.example.matchapi.keword.service.KeywordService; +import com.example.matchcommon.reponse.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/keywords") +@Tag(name = "ADMIN-07-Keyword 추천 검색 키워드 🔎",description = "검색 키워드 API") +public class AdminKeywordController { + private final KeywordService keywordService; + private final AdminKeywordService adminKeywordService; + + @GetMapping("") + @Operation(summary = "07-01 ADMIN Keyword 추천 조회🔎") + public CommonResponse> getKeywordList(){ + return CommonResponse.onSuccess(keywordService.getKeywordList()); + } + + @PostMapping("") + @Operation(summary = "07-02 ADMIN Keyword 업로드🔎") + public CommonResponse> postKeywordList( + @RequestBody AdminKeywordReq.KeywordUpload keyword + ){ + + return CommonResponse.onSuccess(adminKeywordService.postKeyword(keyword)); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/convertor/AdminKeywordConvertor.java b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/convertor/AdminKeywordConvertor.java new file mode 100644 index 00000000..2d54d49d --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/convertor/AdminKeywordConvertor.java @@ -0,0 +1,12 @@ +package com.example.matchapi.admin.keyword.convertor; + +import com.example.matchapi.admin.keyword.dto.AdminKeywordReq; +import com.example.matchcommon.annotation.Convertor; +import com.example.matchdomain.keyword.entity.SearchKeyword; + +@Convertor +public class AdminKeywordConvertor { + public SearchKeyword convertToKeyword(AdminKeywordReq.KeywordUpload keyword) { + return SearchKeyword.builder().keyword(keyword.getSearchKeyword()).priority(keyword.getPriority()).build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/dto/AdminKeywordReq.java b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/dto/AdminKeywordReq.java new file mode 100644 index 00000000..f898c6fe --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/dto/AdminKeywordReq.java @@ -0,0 +1,18 @@ +package com.example.matchapi.admin.keyword.dto; + +import lombok.*; + +import java.io.Serializable; + +public class AdminKeywordReq { + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class KeywordUpload implements Serializable { + private String searchKeyword; + + private int priority; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/keyword/service/AdminKeywordService.java b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/service/AdminKeywordService.java new file mode 100644 index 00000000..399a0543 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/keyword/service/AdminKeywordService.java @@ -0,0 +1,43 @@ +package com.example.matchapi.admin.keyword.service; + +import com.example.matchapi.admin.keyword.convertor.AdminKeywordConvertor; +import com.example.matchapi.admin.keyword.dto.AdminKeywordReq; +import com.example.matchapi.keword.convertor.KeywordConvertor; +import com.example.matchapi.keword.dto.KeywordRes; +import com.example.matchdomain.keyword.entity.SearchKeyword; +import com.example.matchdomain.keyword.repository.SearchKeywordRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class AdminKeywordService { + private final SearchKeywordRepository searchKeywordRepository; + private final KeywordConvertor keywordConvertor; + private final AdminKeywordConvertor adminKeywordConvertor; + + @Transactional + @CachePut(cacheNames = "keywordList", key = "'all'") + public List postKeyword(AdminKeywordReq.KeywordUpload keyword) { + searchKeywordRepository.save(adminKeywordConvertor.convertToKeyword(keyword)); + return cachingKeywordList(); + } + + @CacheEvict(cacheNames = "keywordList") + public List cachingKeywordList(){ + List searchKeywords = searchKeywordRepository.findAllByOrderByPriorityAsc(); + List keywordLists = new ArrayList<>(); + + searchKeywords.forEach( + result -> keywordLists.add(keywordConvertor.convertToKeywordList(result)) + ); + return keywordLists; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/notice/controller/AdminNoticeController.java b/Match-Api/src/main/java/com/example/matchapi/admin/notice/controller/AdminNoticeController.java new file mode 100644 index 00000000..74f9f52d --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/notice/controller/AdminNoticeController.java @@ -0,0 +1,35 @@ +package com.example.matchapi.admin.notice.controller; + +import com.example.matchapi.admin.event.dto.EventUploadReq; +import com.example.matchapi.admin.notice.dto.NoticeUploadReq; +import com.example.matchapi.admin.notice.mapper.AdminNoticeMapper; +import com.example.matchapi.admin.notice.service.AdminNoticeService; +import com.example.matchapi.common.model.ContentsList; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchdomain.notice.entity.Notice; +import com.example.matchdomain.notice.entity.NoticeContent; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +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; + +import javax.validation.Valid; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/notices") +@Tag(name = "ADMIN-10-Notice📝 관리자 공지사항 관리 API") +public class AdminNoticeController { + private final AdminNoticeService adminNoticeService; + private final AdminNoticeMapper mapper = AdminNoticeMapper.INSTANCE; + @PostMapping("") + @Operation(summary = "ADMIN-10-01 공지사항 업로드") + public CommonResponse uploadNoticeList(@Valid @RequestBody NoticeUploadReq noticeUploadReq){ + adminNoticeService.uploadNoticeList(mapper.toEntityNoticeContents(noticeUploadReq.getContentsList()), mapper.toEntityNotice(noticeUploadReq)); + return CommonResponse.onSuccess("업로드 성공"); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/notice/dto/NoticeUploadReq.java b/Match-Api/src/main/java/com/example/matchapi/admin/notice/dto/NoticeUploadReq.java new file mode 100644 index 00000000..63e74cad --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/notice/dto/NoticeUploadReq.java @@ -0,0 +1,20 @@ +package com.example.matchapi.admin.notice.dto; + +import com.example.matchapi.common.model.ContentsList; +import com.example.matchdomain.notice.enums.NoticeType; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class NoticeUploadReq { + private String title; + + private NoticeType noticeType; + + private List contentsList; +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/notice/mapper/AdminNoticeMapper.java b/Match-Api/src/main/java/com/example/matchapi/admin/notice/mapper/AdminNoticeMapper.java new file mode 100644 index 00000000..ae0793a0 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/notice/mapper/AdminNoticeMapper.java @@ -0,0 +1,19 @@ +package com.example.matchapi.admin.notice.mapper; + +import com.example.matchapi.admin.notice.dto.NoticeUploadReq; +import com.example.matchapi.common.model.ContentsList; +import com.example.matchdomain.notice.entity.Notice; +import com.example.matchdomain.notice.entity.NoticeContent; +import org.mapstruct.*; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface AdminNoticeMapper { + AdminNoticeMapper INSTANCE = Mappers.getMapper(AdminNoticeMapper.class); + + List toEntityNoticeContents(List contentsList); + + Notice toEntityNotice(NoticeUploadReq noticeUploadReq); +} diff --git a/Match-Api/src/main/java/com/example/matchapi/admin/notice/service/AdminNoticeService.java b/Match-Api/src/main/java/com/example/matchapi/admin/notice/service/AdminNoticeService.java new file mode 100644 index 00000000..b019f4bf --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/admin/notice/service/AdminNoticeService.java @@ -0,0 +1,29 @@ +package com.example.matchapi.admin.notice.service; + +import com.example.matchdomain.notice.entity.Notice; +import com.example.matchdomain.notice.entity.NoticeContent; +import com.example.matchdomain.notice.repository.NoticeContentRepository; +import com.example.matchdomain.notice.repository.NoticeRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class AdminNoticeService { + private final NoticeRepository noticeRepository; + private final NoticeContentRepository noticeContentRepository; + + @CacheEvict(value = "noticeCache", allEntries = true) + public void uploadNoticeList(List noticeContents, Notice notice) { + notice = noticeRepository.save(notice); + + for(NoticeContent noticeContent : noticeContents){ + noticeContent.setNoticeId(notice.getId()); + } + + noticeContentRepository.saveAll(noticeContents); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/controller/BannerController.java b/Match-Api/src/main/java/com/example/matchapi/banner/controller/BannerController.java index 30261711..484d7e5e 100644 --- a/Match-Api/src/main/java/com/example/matchapi/banner/controller/BannerController.java +++ b/Match-Api/src/main/java/com/example/matchapi/banner/controller/BannerController.java @@ -1,15 +1,32 @@ package com.example.matchapi.banner.controller; +import com.example.matchapi.banner.dto.BannerRes; +import com.example.matchapi.banner.service.BannerService; +import com.example.matchcommon.reponse.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RestController @RequestMapping("/banners") @Slf4j @RequiredArgsConstructor @Tag(name = "06-Banner💳", description = "배너 API") public class BannerController { + private final BannerService bannerService; + + @GetMapping + @Operation( + summary = "06-01 배너 조회", + description = "BannerType 이 Event 면 eventId 사용후 이벤트 페이지로 이동,\n" + + "Contents 면 contentsUrl 사용후 링크로 이동") + public CommonResponse> getBannerList(){ + return CommonResponse.onSuccess(bannerService.getBannerList()); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/convertor/BannerConvertor.java b/Match-Api/src/main/java/com/example/matchapi/banner/convertor/BannerConvertor.java index 8e604c51..8e2f46d4 100644 --- a/Match-Api/src/main/java/com/example/matchapi/banner/convertor/BannerConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/banner/convertor/BannerConvertor.java @@ -1,7 +1,57 @@ package com.example.matchapi.banner.convertor; +import com.example.matchapi.banner.dto.BannerReq; +import com.example.matchapi.banner.dto.BannerRes; import com.example.matchcommon.annotation.Convertor; +import com.example.matchdomain.banner.entity.Banner; +import com.example.matchdomain.banner.enums.BannerType; + +import java.util.ArrayList; +import java.util.List; @Convertor public class BannerConvertor { + + public Banner convertToBannerUpload(BannerType bannerType, String bannerImg, BannerReq.BannerUpload bannerUploadDto) { + if(bannerType.equals(BannerType.EVENT)){ + return Banner + .builder() + .bannerImg(bannerImg) + .bannerType(bannerType) + .eventId(bannerUploadDto.getEventId()) + .build(); + } + else{ + return Banner + .builder() + .bannerImg(bannerImg) + .bannerType(bannerType) + .contentsUrl(bannerUploadDto.getContentsUrl()) + .build(); + } + } + + public List convertToBannerList(List banners) { + List bannerLists = new ArrayList<>(); + + banners.forEach( + result -> + bannerLists.add( + convertToBannerInfo(result) + ) + ); + + return bannerLists; + } + + private BannerRes.BannerList convertToBannerInfo(Banner result) { + return BannerRes.BannerList + .builder() + .bannerId(result.getId()) + .bannerType(result.getBannerType()) + .bannerImg(result.getBannerImg()) + .eventId(result.getEventId()) + .contentsUrl(result.getContentsUrl()) + .build(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerReq.java b/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerReq.java new file mode 100644 index 00000000..26359a67 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerReq.java @@ -0,0 +1,17 @@ +package com.example.matchapi.banner.dto; + +import lombok.*; + +public class BannerReq { + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class BannerUpload { + private Long eventId; + + private String contentsUrl; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerRes.java b/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerRes.java new file mode 100644 index 00000000..1cc454da --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/banner/dto/BannerRes.java @@ -0,0 +1,27 @@ +package com.example.matchapi.banner.dto; + +import com.example.matchdomain.banner.enums.BannerType; +import lombok.*; + +import java.io.Serializable; + +public class BannerRes { + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class BannerList implements Serializable { + private Long bannerId; + + private BannerType bannerType; + + private String bannerImg; + + private Long eventId; + + private String contentsUrl; + + + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java b/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java index 9b929e22..cd87d741 100644 --- a/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java +++ b/Match-Api/src/main/java/com/example/matchapi/banner/service/BannerService.java @@ -1,9 +1,24 @@ package com.example.matchapi.banner.service; +import com.example.matchapi.banner.convertor.BannerConvertor; +import com.example.matchapi.banner.dto.BannerRes; +import com.example.matchdomain.banner.adaptor.BannerAdaptor; +import com.example.matchdomain.banner.entity.Banner; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class BannerService { + private final BannerAdaptor bannerAdaptor; + private final BannerConvertor bannerConvertor; + + @Cacheable(cacheNames = "bannerCache", key = "'all'") + public List getBannerList() { + List banners = bannerAdaptor.getBannerList(); + return bannerConvertor.convertToBannerList(banners); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java b/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java index 2db6797e..ec013354 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/ExceptionAdvice.java @@ -3,14 +3,13 @@ import com.example.matchcommon.exception.BaseDynamicException; import com.example.matchcommon.exception.BaseException; import com.example.matchcommon.reponse.CommonResponse; -import com.example.matchinfrastructure.discord.client.DiscordFeignClient; -import com.example.matchinfrastructure.discord.convertor.DiscordConvertor; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.discord.service.DiscordService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.userdetails.User; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -32,31 +31,31 @@ @RestControllerAdvice @RequiredArgsConstructor public class ExceptionAdvice { - private final DiscordFeignClient discordFeignClient; - private final DiscordConvertor discordConvertor; + private final DiscordService discordService; @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public ResponseEntity onConstraintValidationException(ConstraintViolationException e) { - Map errors = e.getConstraintViolations().stream() + public ResponseEntity onConstraintValidationException(ConstraintViolationException exception, @AuthenticationPrincipal User user, HttpServletRequest request) { + Map errors = exception.getConstraintViolations().stream() .collect(Collectors.toMap( violation -> StreamSupport.stream(violation.getPropertyPath().spliterator(), false) .reduce((first, second) -> second) .get().toString(), ConstraintViolation::getMessage )); + getExceptionStackTrace(exception, user, request, errors); return new ResponseEntity<>(CommonResponse.onFailure("REQUEST_ERROR", "요청 형식 에러 result 확인해주세요", errors), null, HttpStatus.BAD_REQUEST); } @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public ResponseEntity onMethodArgumentNotValidException(MethodArgumentNotValidException e) { + public ResponseEntity onMethodArgumentNotValidException(MethodArgumentNotValidException exception, @AuthenticationPrincipal User user, HttpServletRequest request) { Map errors = new LinkedHashMap<>(); - for (FieldError fieldError : e.getBindingResult().getFieldErrors()) { + for (FieldError fieldError : exception.getBindingResult().getFieldErrors()) { String fieldName = fieldError.getField(); String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse(""); @@ -68,31 +67,16 @@ public ResponseEntity onMethodArgumentNotValidException(MethodArgumentNotValidEx errors.put(fieldName, errorMessage); } - return new ResponseEntity<>(CommonResponse.onFailure("REQUEST_ERROR", "요청 형식 에러 result 확인해주세요", errors), null, HttpStatus.BAD_REQUEST); - } - - - - private void getExceptionStackTrace(Exception e, @AuthenticationPrincipal User user, - HttpServletRequest request) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); + getExceptionStackTrace(exception, user, request, errors); - pw.append("\n==========================!!!ERROR TRACE!!!==========================\n"); - pw.append("uri: " + request.getRequestURI() + " " + request.getMethod() + "\n"); - if (user != null) { - pw.append("uid: " + user.getUsername() + "\n"); - } - pw.append(e.getMessage()); - pw.append("\n====================================================================="); - log.error(sw.toString()); + return new ResponseEntity<>(CommonResponse.onFailure("REQUEST_ERROR", "요청 형식 에러 result 확인해주세요", errors), null, HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = BaseException.class) public ResponseEntity onKnownException(BaseException baseException, @AuthenticationPrincipal User user, HttpServletRequest request) { - getExceptionStackTrace(baseException, user, request); + getExceptionStackTrace(baseException, user, request, null); return new ResponseEntity<>(CommonResponse.onFailure(baseException.getErrorReasonHttpStatus().getCode(), baseException.getErrorReasonHttpStatus().getMessage(), baseException.getErrorReasonHttpStatus().getResult()), null, baseException.getErrorReasonHttpStatus().getHttpStatus()); @@ -101,24 +85,55 @@ public ResponseEntity onKnownException(BaseException baseException, @ExceptionHandler(value = BaseDynamicException.class) public ResponseEntity onKnownDynamicException(BaseDynamicException baseDynamicException, @AuthenticationPrincipal User user, HttpServletRequest request) { - getExceptionStackTrace(baseDynamicException, user, request); + getExceptionStackTrace(baseDynamicException, user, request, null); return new ResponseEntity<>(CommonResponse.onFailure(baseDynamicException.getStatus().getErrorReason().getCode(), baseDynamicException.getStatus().getErrorReason().getMessage(), baseDynamicException.getData()), null, baseDynamicException.getStatus().getErrorReasonHttpStatus().getHttpStatus()); } - - /* @ExceptionHandler(value = Exception.class) public ResponseEntity onException(Exception exception, @AuthenticationPrincipal User user, HttpServletRequest request) { - getExceptionStackTrace(exception, user, request); - String returnMessage = discordFeignClient.errorMessage(discordConvertor.Message(user, exception, request)); + getExceptionStackTrace(exception, user, request, null); + if(user==null){ + discordService.sendUnKnownMessage("로그인 되지 않은 유저", exception, request); + } + else{ + discordService.sendUnKnownMessage(user.getUsername(), exception, request); + } + return new ResponseEntity<>(CommonResponse.onFailure("500", exception.getMessage(), null), null, HttpStatus.INTERNAL_SERVER_ERROR); } - */ + private void getExceptionStackTrace(Exception e, User user, HttpServletRequest request, Map errors) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + pw.append("\n==========================!!!ERROR TRACE!!!==========================\n"); + if(user != null) { + pw.append("USER_ID : " + user.getId()).append("\n"); + } + pw.append("REQUEST_URI : ").append(request.getRequestURI()).append(" ").append(request.getMethod()).append("\n"); + if (e instanceof BaseException) { + pw.append("ERROR_CODE : ").append(((BaseException) e).getErrorReason().getCode()).append("\n"); + pw.append("ERROR_MESSAGE : ").append(((BaseException) e).getErrorReason().getMessage()).append("\n"); + } else if (e instanceof BaseDynamicException) { + pw.append("ERROR_CODE : ").append(((BaseDynamicException) e).getStatus().getErrorReason().getCode()).append("\n"); + pw.append("ERROR_MESSAGE : ").append(((BaseDynamicException) e).getStatus().getErrorReason().getMessage()).append("\n"); + } else if(e instanceof MethodArgumentNotValidException | e instanceof ConstraintViolationException){ + pw.append("ERROR_CODE : ").append("REQUEST_ERROR").append("\n"); + pw.append("ERROR_MESSAGE : ").append(errors.toString()).append("\n"); + } + else { + pw.append("ERROR_MESSAGE : ").append(e.getMessage()).append("\n"); + pw.append("ERROR_MESSAGE : ").append(e.getLocalizedMessage()).append("\n"); + } + pw.append("====================================================================="); + log.error(sw.toString()); + } + + } diff --git a/Match-Api/src/main/java/com/example/matchapi/common/HealthController.java b/Match-Api/src/main/java/com/example/matchapi/common/HealthController.java index d1c4f741..2ddfa90b 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/HealthController.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/HealthController.java @@ -1,11 +1,13 @@ package com.example.matchapi.common; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/health") +@Tag(name = "Health Check 👋") public class HealthController { @GetMapping("") public String healthCheck(){ diff --git a/Match-Api/src/main/java/com/example/matchapi/common/HtmlController.java b/Match-Api/src/main/java/com/example/matchapi/common/HtmlController.java new file mode 100644 index 00000000..d57ca4d3 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/HtmlController.java @@ -0,0 +1,22 @@ +package com.example.matchapi.common; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequestMapping("/terms") +public class HtmlController { + @GetMapping("/policy") + public String showPrivacyPolicy(){ + return "PrivacyPolicy"; + } + + @GetMapping("/service") + public String showTermsConditions(){ + return "TermsConditions"; + } + + @GetMapping("/marketing") + public String showMarketing(){ return "Marketing";} +} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/ImageController.java b/Match-Api/src/main/java/com/example/matchapi/common/ImageController.java new file mode 100644 index 00000000..e79f3d12 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/ImageController.java @@ -0,0 +1,27 @@ +package com.example.matchapi.common; + +import com.example.matchapi.admin.event.service.AdminEventService; +import com.example.matchapi.common.model.UploadFolder; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchinfrastructure.config.s3.S3UploadService; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +@Tag(name = "IMG 컨트롤러") +@RestController +@RequestMapping("/admin/img") +@RequiredArgsConstructor +public class ImageController { + private final S3UploadService s3UploadService; + @PostMapping(value = "", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public CommonResponse imgUpload( + @ModelAttribute UploadFolder uploadFolder, + @ModelAttribute MultipartFile imgFile){ + String url = s3UploadService.uploadOneImg(uploadFolder.getFolder(), imgFile); + return CommonResponse.onSuccess(url); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java b/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java deleted file mode 100644 index 3cb576c0..00000000 --- a/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.example.matchapi.common; - -import com.example.matchapi.order.helper.DataEncrypt; -import com.example.matchapi.order.service.OrderService; -import com.example.matchcommon.properties.NicePayProperties; -import com.example.matchdomain.redis.entity.OrderRequest; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.swagger.v3.oas.annotations.Operation; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -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.client.RestTemplate; -import org.springframework.web.servlet.view.RedirectView; - -import java.io.IOException; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -@Controller -@RequiredArgsConstructor -@Slf4j -public class NicepayController { - private final OrderService orderService; - private final NicePayProperties nicePayProperties; - @Value("${web.return.url}") - private String redirectUrl; - @Value("${server.host}") - private String serverHost; - private final DataEncrypt dataEncrypt; - - /* - @RequestMapping("") - public String indexDemo( - @RequestParam String method, - @RequestParam String orderId, - @RequestParam String productName, - @RequestParam int amount, - Model model) { - model.addAttribute("method",method); - model.addAttribute("orderId", orderId); - model.addAttribute("productName", productName); - model.addAttribute("amount", amount); - model.addAttribute("clientId", nicePayProperties.getClient()); - model.addAttribute("returnUrl",serverHost+"/serverAuth"); - return "index"; - return "index"; - } - - @RequestMapping("/goPay") - public String goPay( - @RequestParam(required = false) String method, - @RequestParam String orderId, - @RequestParam int amount, - Model model - ){ - String ediDate = orderService.getyyyyMMddHHmmss(); - String hashString = dataEncrypt.encrypt(ediDate + orderId + amount + nicePayProperties.getKey()); - model.addAttribute("payMethod", "CreditCard"); - model.addAttribute("goodsName", "Sample Product"); - model.addAttribute("Amt", amount); - model.addAttribute("mid", nicePayProperties.getMid()); - model.addAttribute("moid", orderId); - model.addAttribute("EdiDate", ediDate); - model.addAttribute("signData", hashString); - // model.addAttribute("ReturnURL", "http://localhost:8080/nicepay3.0_utf-8/payResult_utf.jsp"); - return "gopay"; - } - - @RequestMapping("/serverAuth") - public RedirectView requestPaymentAuth( - @RequestParam String tid, - @RequestParam Long amount) throws IOException { - log.info("04-01 Order 결제 인증용 API 결제 ID: " + tid + " 결제 금액 " + amount); - log.info("URL : " + redirectUrl); - orderService.requestPaymentAuth(tid, amount); - RedirectView redirectView = new RedirectView(); - redirectView.setUrl(redirectUrl+"/auth/payComplete/once"); - return redirectView; - } - - */ -} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/TestController.java b/Match-Api/src/main/java/com/example/matchapi/common/TestController.java index 1afaa257..413a04d2 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/TestController.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/TestController.java @@ -1,15 +1,17 @@ package com.example.matchapi.common; import com.example.matchapi.common.aop.CheckIdExist; +import com.example.matchapi.notification.service.NotificationService; import com.example.matchapi.order.helper.OrderHelper; import com.example.matchcommon.reponse.CommonResponse; import com.example.matchcommon.service.MailService; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import com.example.matchinfrastructure.fcm.service.FcmNotificationService; import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -21,6 +23,8 @@ public class TestController { private final OrderHelper orderHelper; private final MailService mailService; + private final NotificationService notificationService; + private final FcmNotificationService fcmNotificationService; @GetMapping("") public void exRedirect3(HttpServletResponse httpServletResponse) throws IOException { @@ -32,6 +36,25 @@ public void exRedirect3(HttpServletResponse httpServletResponse) throws IOExcept public CommonResponse getTest(@PathVariable Long projectId, @PathVariable Long userId, @PathVariable Long donationId){ return CommonResponse.onSuccess("성공"); } + + @PostMapping("/fcm") + public String fcmTest( + @RequestBody FCMNotificationRequestDto fcmNotificationRequestDto + ){ + fcmNotificationService.testNotification(fcmNotificationRequestDto); + return "성공"; + } + + @PostMapping("/fcm/user") + public String fcmUserTest( + @AuthenticationPrincipal User user, + @RequestBody FCMNotificationRequestDto fcmNotificationRequestDto + ){ + fcmNotificationService.testNotification(fcmNotificationRequestDto); + notificationService.saveTestNotification(user, fcmNotificationRequestDto); + + return "성공"; + } /* @GetMapping("/email") diff --git a/Match-Api/src/main/java/com/example/matchapi/common/model/AlarmType.java b/Match-Api/src/main/java/com/example/matchapi/common/model/AlarmType.java new file mode 100644 index 00000000..db363b5b --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/model/AlarmType.java @@ -0,0 +1,5 @@ +package com.example.matchapi.common.model; + +public enum AlarmType { + SERVICE,EVENT +} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/model/ContentsList.java b/Match-Api/src/main/java/com/example/matchapi/common/model/ContentsList.java new file mode 100644 index 00000000..1c142f21 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/model/ContentsList.java @@ -0,0 +1,20 @@ +package com.example.matchapi.common.model; + +import com.example.matchdomain.common.model.ContentsType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import javax.validation.constraints.NotBlank; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ContentsList{ + @NotBlank(message = "Contents 타입을 입력해주새요") + private ContentsType contentsType; + + @Schema(description = "이미지 url or Text", required = false) + private String contents; +} diff --git a/Match-Api/src/main/java/com/example/matchapi/common/model/UploadFolder.java b/Match-Api/src/main/java/com/example/matchapi/common/model/UploadFolder.java new file mode 100644 index 00000000..43345457 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/model/UploadFolder.java @@ -0,0 +1,12 @@ +package com.example.matchapi.common.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum UploadFolder { + EVENT("event"), NOTICE("notice"); + + private final String folder; +} diff --git a/Match-Api/src/main/java/com/example/matchapi/security/JwtAccessDeniedHandler.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAccessDeniedHandler.java similarity index 96% rename from Match-Api/src/main/java/com/example/matchapi/security/JwtAccessDeniedHandler.java rename to Match-Api/src/main/java/com/example/matchapi/common/security/JwtAccessDeniedHandler.java index 0bf339d9..f36f24be 100644 --- a/Match-Api/src/main/java/com/example/matchapi/security/JwtAccessDeniedHandler.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAccessDeniedHandler.java @@ -1,4 +1,4 @@ -package com.example.matchapi.security; +package com.example.matchapi.common.security; import com.example.matchdomain.user.exception.UserAuthErrorCode; import org.json.JSONException; @@ -6,7 +6,6 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; diff --git a/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAuthenticationEntryPoint.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAuthenticationEntryPoint.java new file mode 100644 index 00000000..121b8e89 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtAuthenticationEntryPoint.java @@ -0,0 +1,91 @@ +package com.example.matchapi.common.security; + +import com.example.matchdomain.user.exception.UserAuthErrorCode; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.rmi.ServerException; + +import static com.example.matchdomain.user.exception.UserAuthErrorCode.*; + +@Component +@Slf4j +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) throws IOException, ServerException { + // 유효한 자격증명을 제공하지 않고 접근하려 할때 401 + String exception = (String) request.getAttribute("exception"); + + UserAuthErrorCode errorCode; + + if(exception == null) { + errorCode = UNAUTHORIZED_EXCEPTION; + setResponse(response, errorCode, request); + return; + } + + switch (exception) { + case "NotExistUser": + errorCode = NOT_EXIST_USER; + setResponse(response, errorCode, request); + return; + case "ExpiredJwtException": + errorCode = EXPIRED_JWT_EXCEPTION; + setResponse(response, errorCode, request); + return; + case "MalformedJwtException": + errorCode = INVALID_TOKEN_EXCEPTION; + setResponse(response, errorCode, request); + return; + case "HijackException": + errorCode = HIJACK_JWT_TOKEN_EXCEPTION; + setResponse(response, errorCode, request); + return; + case "NoSuchElementException": + errorCode = NOT_EXISTS_USER_HAVE_TOKEN; + setResponse(response, errorCode, request); + return; + } + } + + private void setResponse(HttpServletResponse response, UserAuthErrorCode errorCode, HttpServletRequest request) throws IOException { + JSONObject json = new JSONObject(); + response.setContentType("application/json;charset=UTF-8"); + response.setCharacterEncoding("utf-8"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + try { + json.put("code", errorCode.getCode()); + json.put("message", errorCode.getMessage()); + json.put("isSuccess",false); + } catch (JSONException e) { + e.printStackTrace(); + } + + + String requestUri = request.getRequestURI(); + if(!requestUri.equals("/error")) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.append("\n==========================!!!JWT ERROR TRACE!!!==========================\n"); + pw.append("REQUEST_URI : ").append(request.getRequestURI()).append(" ").append(request.getMethod()).append("\n"); + pw.append("ERROR_CODE : ").append(errorCode.getCode()).append("\n"); + pw.append("ERROR_MESSAGE : ").append(errorCode.getMessage()).append("\n"); + pw.append("========================================================================="); + log.error(sw.toString()); + } + response.getWriter().print(json); + } +} \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/security/JwtFilter.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtFilter.java similarity index 89% rename from Match-Api/src/main/java/com/example/matchapi/security/JwtFilter.java rename to Match-Api/src/main/java/com/example/matchapi/common/security/JwtFilter.java index d6cdf34d..316971f9 100644 --- a/Match-Api/src/main/java/com/example/matchapi/security/JwtFilter.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtFilter.java @@ -1,4 +1,4 @@ -package com.example.matchapi.security; +package com.example.matchapi.common.security; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; @@ -40,11 +40,10 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo Authentication authentication = jwtService.getAuthentication(jwt,servletRequest); SecurityContextHolder.getContext().setAuthentication(authentication); if(authentication !=null) { - logger.info("Security Context에 '{}' 인증 정보를 저장했습니다, uri: {} method: {}", authentication.getName(), requestURI, httpServletRequest.getMethod()); + logger.info("Security Context '{}' 인증 정보를 저장했습니다, uri: {} method: {}", authentication.getName(), requestURI, httpServletRequest.getMethod()); }else{ logger.info("해당 토큰을 가진 유저가 존재하지 않습니다, uri: {}", requestURI); } - } else { logger.info("유효한 JWT 토큰이 없습니다, uri: {}", requestURI); } diff --git a/Match-Api/src/main/java/com/example/matchapi/security/JwtSecurityConfig.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtSecurityConfig.java similarity index 94% rename from Match-Api/src/main/java/com/example/matchapi/security/JwtSecurityConfig.java rename to Match-Api/src/main/java/com/example/matchapi/common/security/JwtSecurityConfig.java index 64d859a4..b2498f12 100644 --- a/Match-Api/src/main/java/com/example/matchapi/security/JwtSecurityConfig.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtSecurityConfig.java @@ -1,4 +1,4 @@ -package com.example.matchapi.security; +package com.example.matchapi.common.security; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; diff --git a/Match-Api/src/main/java/com/example/matchapi/security/JwtService.java b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtService.java similarity index 95% rename from Match-Api/src/main/java/com/example/matchapi/security/JwtService.java rename to Match-Api/src/main/java/com/example/matchapi/common/security/JwtService.java index 6c7b96ef..5d27e1e5 100644 --- a/Match-Api/src/main/java/com/example/matchapi/security/JwtService.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/JwtService.java @@ -1,6 +1,5 @@ -package com.example.matchapi.security; +package com.example.matchapi.common.security; -import com.example.matchapi.user.convertor.UserConvertor; import com.example.matchapi.user.dto.UserRes; import com.example.matchcommon.properties.JwtProperties; import com.example.matchdomain.redis.entity.AccessToken; @@ -29,9 +28,6 @@ import java.util.NoSuchElementException; import java.util.Optional; -import static com.example.matchapi.security.JwtFilter.AUTHORIZATION_HEADER; -import static com.example.matchapi.security.JwtFilter.REFRESH_TOKEN_HEADER; - @RequiredArgsConstructor @Component @Slf4j @@ -168,12 +164,12 @@ public boolean validateToken(ServletRequest servletRequest, String token) { public String getJwt(){ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); - return request.getHeader(AUTHORIZATION_HEADER); + return request.getHeader(JwtFilter.AUTHORIZATION_HEADER); } public String getRefreshToken(){ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); - return request.getHeader(REFRESH_TOKEN_HEADER); + return request.getHeader(JwtFilter.REFRESH_TOKEN_HEADER); } public Date getExpiredTime(String token){ diff --git a/Match-Api/src/main/java/com/example/matchapi/security/UserDetailsServiceImpl.java b/Match-Api/src/main/java/com/example/matchapi/common/security/UserDetailsServiceImpl.java similarity index 97% rename from Match-Api/src/main/java/com/example/matchapi/security/UserDetailsServiceImpl.java rename to Match-Api/src/main/java/com/example/matchapi/common/security/UserDetailsServiceImpl.java index fcf60951..6ce6099e 100644 --- a/Match-Api/src/main/java/com/example/matchapi/security/UserDetailsServiceImpl.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/security/UserDetailsServiceImpl.java @@ -1,4 +1,4 @@ -package com.example.matchapi.security; +package com.example.matchapi.common.security; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.repository.UserRepository; diff --git a/Match-Api/src/main/java/com/example/matchapi/common/util/TimeHelper.java b/Match-Api/src/main/java/com/example/matchapi/common/util/TimeHelper.java new file mode 100644 index 00000000..cd51f65f --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/common/util/TimeHelper.java @@ -0,0 +1,46 @@ +package com.example.matchapi.common.util; + +import com.example.matchcommon.annotation.Helper; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +import static com.example.matchdomain.event.enums.EventStatus.FINISH; +import static com.example.matchdomain.event.enums.EventStatus.UNDER; + +@Helper +public class TimeHelper { + + public String timeFormat(LocalDateTime createdAt) { + return createdAt.getYear() + "." + createdAt.getMonthValue() + "." + createdAt.getDayOfMonth(); + } + + public String dayTimeFormat(LocalDateTime createdAt) { + return createdAt.getYear() + "." + createdAt.getMonthValue() + "." + createdAt.getDayOfMonth() + " " + checkTimes(createdAt.getHour()) + ":" + checkTimes(createdAt.getMinute()); + } + + public String matchTimeFormat(LocalDateTime createdAt) { + return createdAt.getYear() + "." + checkTimes(createdAt.getMonthValue()) + "." + checkTimes(createdAt.getDayOfMonth()); + } + + public String checkTimes(int value){ + if(value < 10){ + return "0" + value; + } + else{ + return String.valueOf(value); + } + } + + public String checkFinishStatus(LocalDate eventEndDate) { + LocalDate nowDate = LocalDate.now(); + + if(nowDate.isBefore(eventEndDate)){ + return UNDER.getValue(); + } + else{ + return FINISH.getValue(); + } + + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java b/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java index bd8327de..9b17c21b 100644 --- a/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java +++ b/Match-Api/src/main/java/com/example/matchapi/config/SecurityConfig.java @@ -1,12 +1,10 @@ package com.example.matchapi.config; -import com.example.matchapi.security.*; +import com.example.matchapi.common.security.*; import com.example.matchdomain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; @@ -91,6 +89,7 @@ protected void configure(HttpSecurity httpSecurity) throws Exception { .antMatchers("/order/serverAuth").permitAll() .antMatchers("/projects").permitAll() .antMatchers("/projects/**").permitAll() + .antMatchers("/projects/list").authenticated() .antMatchers("/").permitAll() .antMatchers("/serverAuth").permitAll() .antMatchers(HttpMethod.GET,"/donation-temporaries").permitAll() @@ -103,6 +102,8 @@ protected void configure(HttpSecurity httpSecurity) throws Exception { .antMatchers("/admin/donation-temporaries/**").hasAnyRole("ADMIN") .antMatchers("/admin/order/**").hasAnyRole("ADMIN") .antMatchers("/admin/auth/logIn").permitAll() + .antMatchers("/test/fcm/user").authenticated() + .antMatchers("/terms/**").permitAll() .anyRequest().authenticated() .and() diff --git a/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java b/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java index 8bf4b333..a330f53a 100644 --- a/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java +++ b/Match-Api/src/main/java/com/example/matchapi/config/SwaggerConfig.java @@ -4,6 +4,8 @@ import com.example.matchcommon.annotation.ApiErrorCodeExample; import com.example.matchcommon.dto.ErrorReason; import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.v3.core.jackson.ModelResolver; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.examples.Example; @@ -17,30 +19,43 @@ import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.SpringDocUtils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.method.HandlerMethod; import org.springdoc.core.customizers.OperationCustomizer; import java.util.*; import java.util.stream.Collectors; -import static java.lang.Integer.parseInt; -import static java.util.stream.Collectors.groupingBy; - @Configuration @RequiredArgsConstructor +@Slf4j public class SwaggerConfig { //jwt 토큰 인증을 위한 버튼까지 포함 + static { + SpringDocUtils.getConfig().addAnnotationsToIgnore(AuthenticationPrincipal.class, CookieValue.class); + } private final ApplicationContext applicationContext; + @Value("${spring.config.activate.on-profile}") + private String profile; + + @Bean + public ModelResolver modelResolver(ObjectMapper objectMapper) { + return new ModelResolver(objectMapper); + } @Bean public OpenAPI openAPI() { Info info = new Info() - .title("Match Aligo Rest API 문서") // 타이틀 + .title(profile + "환경 Match Rest API 문서") // 타이틀 .version("0.0.1") // 문서 버전 .description("잘못된 부분이나 오류 발생 시 바로 말씀해주세요.") // 문서 설명 .contact(new Contact() // 연락처 diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java b/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java index ef32ee03..88ea4703 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/controller/DonationController.java @@ -2,6 +2,7 @@ import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.service.DonationService; +import com.example.matchapi.user.dto.UserRes; import com.example.matchcommon.annotation.ApiErrorCodeExample; import com.example.matchcommon.reponse.CommonResponse; import com.example.matchcommon.reponse.PageResponse; @@ -36,6 +37,7 @@ public CommonResponse refundDonation( return CommonResponse.onSuccess("기부금 환불 성공"); } + /* @GetMapping("/flame/filter") @Deprecated @ApiErrorCodeExample(UserAuthErrorCode.class) @@ -51,6 +53,8 @@ public CommonResponse>> getFlameList( return CommonResponse.onSuccess(donationService.getFlameList(user, page, size, flame, order, content)); } + */ + @DeleteMapping("/{regularId}") @ApiErrorCodeExample({UserAuthErrorCode.class, CancelRegularPayErrorCode.class}) @Operation(summary = "05-04 Donation💸 정기 결제 해지 API") @@ -82,7 +86,7 @@ public CommonResponse>> getDonationL return CommonResponse.onSuccess(donationService.getDonationList(user.getId(), filter, page, size)); } - @GetMapping("/burning-match") +/* @GetMapping("/burning-match") @ApiErrorCodeExample({UserAuthErrorCode.class}) @Operation(summary = "05-06 유저의 불타는 매치 #FRAME_홈_불타는 매치") public CommonResponse>> getBurningMatch( @@ -91,9 +95,9 @@ public CommonResponse>> getBurnin @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "5") int size ) { return CommonResponse.onSuccess(donationService.getBurningMatch(user, page, size)); - } + }*/ - @GetMapping("/top/{regularPayId}") + /* @GetMapping("/top/{regularPayId}") @ApiErrorCodeExample({UserAuthErrorCode.class, GetRegularErrorCode.class}) @Operation(summary = "05-07-01 후원 상세 보기 조회 #FRAME_불타는 매치_후원_상세_보기_상단조회") public CommonResponse getDonationRegular(@PathVariable Long regularPayId, @@ -112,7 +116,7 @@ public CommonResponse>> getDo @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "5") int size ) { return CommonResponse.onSuccess(donationService.getDonationRegularList(regularPayId, user, page, size)); - } + }*/ @GetMapping("/pay/{regularPayId}") @ApiErrorCodeExample({UserAuthErrorCode.class, GetRegularErrorCode.class}) @@ -156,4 +160,27 @@ public CommonResponse>> getFl return CommonResponse.onSuccess(donationService.getFlameRegularList(donationId, user, page, size)); } + @Operation(summary = "05-11 진행 중인 매치 조회 #FRAME_기부 내역",description = "진행중인 매치 조회 API 입니다.") + @GetMapping("/match") + @ApiErrorCodeExample({UserAuthErrorCode.class}) + public CommonResponse>> getUserMatchList( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "10") int size + ){ + PageResponse> matchLists = donationService.getUserMatchList(user, page, size); + return CommonResponse.onSuccess(matchLists); + } + + @Operation(summary = "05-12 타오르는 불꽃이 리스트 조회 #FRAME_불꽃이_둘러보기", description = "타오르는 불꽃이 리스트 조회입니다, 후원중인 곳이 없을 때 빈 리스트가 반환됩니다.") + @GetMapping("/burning-flame") + @ApiErrorCodeExample(UserAuthErrorCode.class) + public CommonResponse>> getBurningFlameList( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "10") int size + ){ + return CommonResponse.onSuccess(donationService.getBurningFlameList(user, page, size)); + } + } \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationConvertor.java b/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationConvertor.java index a0fc9ae0..6847b7e4 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationConvertor.java @@ -1,5 +1,6 @@ package com.example.matchapi.donation.convertor; +import com.example.matchapi.common.util.TimeHelper; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; import com.example.matchapi.project.dto.ProjectRes; @@ -9,6 +10,7 @@ import com.example.matchdomain.donation.repository.DonationUserRepository; import com.example.matchdomain.donation.repository.HistoryImageRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import java.util.ArrayList; import java.util.List; @@ -23,22 +25,9 @@ @RequiredArgsConstructor public class DonationConvertor { private final DonationHelper donationHelper; - private final HistoryImageRepository historyImageRepository; - public DonationRes.FlameList Flame(DonationUser result) { - return DonationRes.FlameList.builder() - .donationId(result.getId()) - .amount(result.getPrice()) - .donationStatus(result.getDonationStatus().getName()) - .flameName(result.getInherenceName()) - .createdAt(result.getCreatedAt().getYear()+"년 " - +result.getCreatedAt().getMonthValue()+"월 " - +result.getCreatedAt().getDayOfMonth()+"일 " - +result.getCreatedAt().getHour()+"시 " - +result.getCreatedAt().getMinute()+"분") - .build(); - } + private final TimeHelper timeHelper; - public DonationRes.DonationCount DonationCount(List donationUserList) { int beforeCnt=0; + public DonationRes.DonationCount convertToDonationCount(List donationUserList) { int beforeCnt=0; int underCnt=0; int successCnt=0; @@ -60,7 +49,7 @@ public DonationRes.FlameList Flame(DonationUser result) { } - public DonationRes.DonationList DonationList(DonationUser result) { + public DonationRes.DonationList convertToDonationListDetail(DonationUser result) { String payDate=""; if(result.getRegularPayment()!=null) { payDate = "매월 " + result.getRegularPayment().getPayDate() + "일 " + donationHelper.parsePriceComma(Math.toIntExact(result.getRegularPayment().getAmount())); @@ -79,14 +68,14 @@ public DonationRes.DonationList DonationList(DonationUser result) { } - public DonationHistory DonationHistory(Long id, HistoryStatus historyStatus) { + public DonationHistory convertToDonationHistory(Long id, HistoryStatus historyStatus) { return DonationHistory.builder() .donationUserId(id) .historyStatus(historyStatus) .build(); } - public DonationRes.DonationRegular DonationRegular(RegularPayment regularPayment) { + public DonationRes.DonationRegular convertToDonationRegular(RegularPayment regularPayment) { return DonationRes.DonationRegular .builder() .projectTitle(regularPayment.getProject().getProjectName()) @@ -97,27 +86,27 @@ public DonationRes.DonationRegular DonationRegular(RegularPayment regularPayment .build(); } - public DonationRes.DonationRegularList DonationRegularList(DonationHistory result) { + public DonationRes.DonationRegularList convertToDonationRegularListDetail(DonationHistory result, String inherenceName) { String histories = ""; String flameImage = null; List donationHistoryImages = new ArrayList<>(); System.out.println("분기"); if(result.getHistoryStatus() == HistoryStatus.CREATE){ System.out.println("CREATE"); - histories = result.getDonationUser().getUser().getName() + "님의 불꽃이 탄생했습니다."; - flameImage= result.getFlameImage(); + histories = inherenceName + "가 생성되었습니다."; + flameImage= result.getDonationUser().getFlameImage(); }else if(result.getHistoryStatus() == HistoryStatus.COMPLETE) { System.out.println("COMPLETE"); histories = "'후원품'을 '후원처'에 전달했습니다."; - donationHistoryImages = DonationHistoryImage(result.getHistoryImages()); + donationHistoryImages = convertToDonationHistoryImage(result.getHistoryImages()); }else{ - histories = result.getCnt() + "명의 불꽃이 후원품으로 변했습니다."; + histories = inherenceName + " 외 " + (result.getCnt()-1) + "마리의 불꽃이들이 '후원품'으로 변했습니다."; } return DonationRes.DonationRegularList .builder() .historyId(result.getId()) - .historyDate(result.getCreatedAt().getYear()+"."+result.getCreatedAt().getMonthValue()+"."+result.getCreatedAt().getDayOfMonth()) + .historyDate(timeHelper.matchTimeFormat(result.getCreatedAt())) .histories(histories) .flameImage(flameImage) .historyStatus(result.getHistoryStatus()) @@ -125,7 +114,7 @@ public DonationRes.DonationRegularList DonationRegularList(DonationHistory resul .build(); } - private List DonationHistoryImage(List historyImages) { + private List convertToDonationHistoryImage(List historyImages) { List donationHistoryImages = new ArrayList<>(); System.out.println(historyImages.size()); @@ -142,21 +131,22 @@ private List DonationHistoryImage(List imgUrlList = null; if(result.getImgUrlList()!=null){ imgUrlList = Stream.of(result.getImgUrlList().split(",")).collect(Collectors.toList()); @@ -172,7 +162,7 @@ public DonationRes.BurningMatchRes BurningMatch(DonationUserRepository.flameList .build(); } - public DonationRes.FlameProjectList FlameProject(DonationUser result) { + public DonationRes.FlameProjectList convertToFlameProject(DonationUser result) { return DonationRes.FlameProjectList .builder() .donationId(result.getId()) @@ -183,33 +173,34 @@ public DonationRes.FlameProjectList FlameProject(DonationUser result) { .build(); } - public DonationRes.DonationFlame DonationFlame(RegularPayment regularPayment, DonationUser donationUser) { + public DonationRes.DonationFlame convertToDonationFlame(int sequence, DonationUser donationUser) { return DonationRes.DonationFlame .builder() + .imgUrl(donationUser.getFlameImage()) + .flameType(donationUser.getFlameType().getType()) .inherenceName(donationUser.getInherenceName()) - .regularPayStatus(regularPayment.getRegularPayStatus()) - .imgUrl(regularPayment.getProject().getProjectImage().get(0).getUrl()) - .regularPayId(regularPayment.getId()) - .payDate(regularPayment.getPayDate()) - .amount(Math.toIntExact(regularPayment.getAmount())) + .usages(donationUser.getProject().getUsages()) + .amount(Math.toIntExact(donationUser.getPrice())) + .sequence(sequence) + .randomMessage(donationHelper.createRandomMessage(donationUser)) .build(); } - public ProjectRes.MatchHistory MatchHistory(DonationHistory result) { + public ProjectRes.MatchHistory convertToMatchHistoryDetail(DonationHistory result) { String histories = ""; String profileImgUrl = ""; String nickname = ""; System.out.println(result.getHistoryStatus()); if(result.getHistoryStatus().equals(HistoryStatus.CREATE)){ - histories = result.getDonationUser().getUser().getName() + "님의 불꽃이 탄생했습니다."; + histories = result.getDonationUser().getInherenceName() + "가 여기에 나타났습니다."; profileImgUrl = result.getDonationUser().getUser().getProfileImgUrl(); nickname = result.getDonationUser().getUser().getNickname(); }else if(result.getHistoryStatus().equals(HistoryStatus.COMPLETE)) { - histories = "'후원품'을 '후원처'에 전달했습니다."; + histories = "'후원품'을 " + result.getProject().getUsages() + "에 전달했습니다."; profileImgUrl = MATCH_PROFILE; nickname = MATCH_NAME; }else if(result.getHistoryStatus().equals(HistoryStatus.CHANGE)){ - histories = result.getCnt() + "명의 불꽃이 후원품으로 변했습니다."; + histories = result.getCnt() + "마리의 불꽃이 후원품으로 변했습니다."; profileImgUrl = MATCH_PROFILE; nickname = MATCH_NAME; }else if(result.getHistoryStatus().equals(HistoryStatus.TURN_ON)){ @@ -221,7 +212,7 @@ public ProjectRes.MatchHistory MatchHistory(DonationHistory result) { profileImgUrl = MATCH_PROFILE; nickname = MATCH_NAME; }else if(result.getHistoryStatus().equals(HistoryStatus.FINISH)){ - histories = "매치가 종료되었습니다.."; + histories = "매치가 종료되었습니다."; profileImgUrl = MATCH_PROFILE; nickname = MATCH_NAME; } @@ -234,14 +225,86 @@ public ProjectRes.MatchHistory MatchHistory(DonationHistory result) { .profileImageUrl(profileImgUrl) .nickname(nickname) .historyStatus(result.getHistoryStatus()) - .historyDate(donationHelper.dayTimeFormat(result.getCreatedAt())) + .historyDate(timeHelper.dayTimeFormat(result.getCreatedAt())) .build(); } - public DonationHistory DonationHistoryTurnOn(Long id, HistoryStatus historyStatus) { + public DonationHistory convertToDonationHistoryTurnOn(Long id, HistoryStatus historyStatus) { return DonationHistory.builder() .regularPaymentId(id) .historyStatus(historyStatus) .build(); } + + public List convertToDonationList(Page donationUsers) { + List donationLists = new ArrayList<>(); + donationUsers.getContent().forEach( + result ->{ + donationLists.add( + convertToDonationListDetail(result) + ); + } + ); + + return donationLists; + } + + public List BurningMatch(List flameLists) { + List burningMatchRes = new ArrayList<>(); + flameLists.forEach( + result -> { + burningMatchRes.add(convertToBurningMatchDetail(result)); + } + ); + + return burningMatchRes; + } + + public List convertToDonationRegularList(List donationHistories, String inherenceName){ + List donationRegularLists = new ArrayList<>(); + donationHistories.forEach( + result -> donationRegularLists.add( + convertToDonationRegularListDetail(result, inherenceName) + ) + ); + return donationRegularLists; + } + + public List convertToMatchHistory(List donationHistories){ + List matchHistories = new ArrayList<>(); + + donationHistories.forEach( + result -> matchHistories.add( + convertToMatchHistoryDetail(result) + ) + ); + + return matchHistories; + } + + public List convertToFlameProjectList(List donationUsers){ + List flameProjectLists = new ArrayList<>(); + + donationUsers.forEach( + result -> flameProjectLists.add( + convertToFlameProject(result) + ) + ); + + return flameProjectLists; + } + + public List convertToPayList(List donationUsers) { + List payLists = new ArrayList<>(); + + donationUsers.forEach( + result -> payLists.add( + convertToPayListDetail(result) + ) + ); + + return payLists; + } + + } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationTemporaryConvertor.java b/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationTemporaryConvertor.java index 8f3b3031..1e30e104 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationTemporaryConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/convertor/DonationTemporaryConvertor.java @@ -13,7 +13,7 @@ @Convertor public class DonationTemporaryConvertor { - public DonationTemporaryRes.UserInfo UserInfo(User user) { + public DonationTemporaryRes.UserInfo convertToUserInfo(User user) { return DonationTemporaryRes.UserInfo .builder() .name(user.getName()) @@ -22,7 +22,7 @@ public DonationTemporaryRes.UserInfo UserInfo(User user) { .build(); } - public DonationTemporary DonationInfo(User user, DonationTemporaryReq.DonationInfo donationInfo) { + public DonationTemporary convertToDonationInfo(User user, DonationTemporaryReq.DonationInfo donationInfo) { return DonationTemporary .builder() .userId(user.getId()) @@ -34,17 +34,17 @@ public DonationTemporary DonationInfo(User user, DonationTemporaryReq.DonationIn .build(); } - public DonationTemporaryRes.DonationList DonationList(DonationList result, String amount) { + public DonationTemporaryRes.DonationList convertToDonationList(DonationList result, String amount) { return DonationTemporaryRes.DonationList .builder() .name(result.getName()) .amount(amount) .donationDate(result.getCreatedAt().getYear()+"."+result.getCreatedAt().getMonthValue()+"."+result.getCreatedAt().getDayOfMonth()+"."+result.getCreatedAt().getHour()+"."+result.getCreatedAt().getMinute()) - .name(replaceMiddleWithAsterisk(result.getName())) + .name(convertToReplaceMiddleWithAsterisk(result.getName())) .build(); } - public String replaceMiddleWithAsterisk(String str) { + public String convertToReplaceMiddleWithAsterisk(String str) { int length = str.length(); if (length == 2) { @@ -56,17 +56,17 @@ public String replaceMiddleWithAsterisk(String str) { return str.substring(0, middle) + "*" + str.substring(middle + 1); } - public List DonationRequestAdminList(List content) { + public List convertToDonationRequestAdminList(List content) { List donationRequestAdminLists = new ArrayList<>(); content.forEach( result -> donationRequestAdminLists.add( - DonationInfoList(result) + convertToDonationInfoList(result) ) ); return donationRequestAdminLists; } - public DonationTemporaryRes.DonationRequestAdminList DonationInfoList(DonationTemporary result) { + public DonationTemporaryRes.DonationRequestAdminList convertToDonationInfoList(DonationTemporary result) { return DonationTemporaryRes.DonationRequestAdminList .builder() .donationRequestId(result.getId()) @@ -80,7 +80,7 @@ public DonationTemporaryRes.DonationRequestAdminList DonationInfoList(DonationTe .build(); } - public DonationList DonationDeposit(DonationTemporaryReq.DonationDeposit donationDeposit) { + public DonationList convertToDonationDeposit(DonationTemporaryReq.DonationDeposit donationDeposit) { return DonationList.builder() .donationTemporaryId(donationDeposit.getDonationRequestId()) .amount(donationDeposit.getAmount()) @@ -88,7 +88,7 @@ public DonationList DonationDeposit(DonationTemporaryReq.DonationDeposit donatio .build(); } - public DonationTemporaryRes.DonationDetail DonationInfoDetail(DonationTemporary donationTemporary) { + public DonationTemporaryRes.DonationDetail convertToDonationInfoDetail(DonationTemporary donationTemporary) { return DonationTemporaryRes.DonationDetail .builder() .donationRequestId(donationTemporary.getId()) @@ -103,7 +103,7 @@ public DonationTemporaryRes.DonationDetail DonationInfoDetail(DonationTemporary .build(); } - public DonationTemporary DonationInfoEmail(User user, DonationTemporaryReq.DonationInfo donationInfo) { + public DonationTemporary convertToDonationInfoEmail(User user, DonationTemporaryReq.DonationInfo donationInfo) { return DonationTemporary .builder() .userId(user.getId()) diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/convertor/RegularPaymentConvertor.java b/Match-Api/src/main/java/com/example/matchapi/donation/convertor/RegularPaymentConvertor.java new file mode 100644 index 00000000..5306b7fe --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/donation/convertor/RegularPaymentConvertor.java @@ -0,0 +1,64 @@ +package com.example.matchapi.donation.convertor; + +import com.example.matchapi.common.util.TimeHelper; +import com.example.matchapi.donation.dto.DonationRes; +import com.example.matchapi.donation.helper.DonationHelper; +import com.example.matchcommon.annotation.Convertor; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.RegularPayment; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Convertor +@RequiredArgsConstructor +public class RegularPaymentConvertor { + private final TimeHelper timeHelper; + private final DonationHelper donationHelper; + public List convertToMatchList(List regularPayments) { + List matchLists = new ArrayList<>(); + + regularPayments.forEach( + result -> matchLists.add(convertToMatchListDetail(result)) + ); + + return matchLists; + } + + private DonationRes.MatchList convertToMatchListDetail(RegularPayment result) { + return DonationRes.MatchList + .builder() + .regularDate(timeHelper.matchTimeFormat(result.getCreatedAt())) + .projectTitle(result.getProject().getProjectName()) + .regularPayStatus(result.getRegularPayStatus()) + .payDate(result.getPayDate()) + .amount(donationHelper.parsePriceComma(Math.toIntExact(result.getAmount()))) + .build(); + } + + public List convertToBurningFlameList(List donationUsers) { + List burningFlameDto = new ArrayList<>(); + + donationUsers.forEach( + result -> burningFlameDto.add( + convertToBurningFlame(result) + ) + ); + + return burningFlameDto; + + } + + private DonationRes.BurningFlameDto convertToBurningFlame(DonationUser donationUser) { + + return DonationRes.BurningFlameDto + .builder() + .donationId(donationUser.getId()) + .usages(donationUser.getProject().getUsages()) + .image(donationUser.getFlameImage()) + .inherenceName(donationUser.getInherenceName()) + .randomMessage(donationHelper.createRandomMessage(donationUser)) + .build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java b/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java index 5792eede..818570b7 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/dto/DonationRes.java @@ -3,9 +3,11 @@ import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.enums.HistoryStatus; import com.example.matchdomain.donation.entity.enums.RegularPayStatus; +import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; +import java.io.Serializable; import java.util.List; public class DonationRes { @@ -161,23 +163,26 @@ public static class DonationRegular { @AllArgsConstructor @NoArgsConstructor public static class DonationFlame { - @Schema(description = "후원 대표 이미지", required = true, example = "이미지 url") + @Schema(description = "불꽃이 이미지", required = true, example = "이미지 url") private String imgUrl; + @Schema(description = "불꽃이 유형", required = true, example = "일반 불꽃") + private String flameType; + @Schema(description = "고유 이름", required = true, example = "불꽃이 고유 이름") private String inherenceName; - @Schema(description = "PROCEEDING = 기부 진행중, PROJECT_FINISH = 프로젝트 마감으로 인한 매칭 종료, USER_CANCEL = 유저 후원 취소") - private RegularPayStatus regularPayStatus; + @Schema(description = "성심수녀회") + private String usages; - @Schema(description = "후원 금액" , required = true, example = "후원 금액") + @Schema(description = "후원 금액(전달된 온도)" , required = true, example = "1000") private int amount; - @Schema(description = "후원 id", required = true, example = "2") - private Long regularPayId; + @Schema(description = "생성 순서 0일시 단일 기부(즉 1회성 기부입니다) 생성 순서 표시 x", required = true, example = "2") + private int sequence; - @Schema(description = "후원 날짜", required = true, example = "2") - private int payDate; + + private String randomMessage; } @Getter @@ -199,9 +204,11 @@ public static class DonationRegularList { private String historyDate; @Schema(description = "불꽃이 이미지", required = true, example = "이미지 url") + @JsonInclude(JsonInclude.Include.NON_EMPTY) private String flameImage; @Schema(description = "전달 사진 리스트 COMPLETE 인 경우에 이미지가 들어갑니다.", required = true, example = "") + @JsonInclude(JsonInclude.Include.NON_EMPTY) private List donationHistoryImages; } @Getter @@ -230,6 +237,8 @@ public static class PayList { private String payMethod; private String amount; + + private String inherenceNumber; } @Getter @@ -259,4 +268,43 @@ public static class FlameProjectList { private String imgUrl; } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class MatchList { + private String projectTitle; + + private String regularDate; + + private RegularPayStatus regularPayStatus; + + private int payDate; + + private String amount; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class BurningFlameDto implements Serializable { + @Schema(description = "불꽃이 id 상세조회시 필요", required = true, example = "2") + private Long donationId; + + @Schema(description = "후원처 명") + private String usages; + + @Schema(description = "고유 불꽃이 이름") + private String inherenceName; + + @Schema(description = "불꽃이 이미지") + private String image; + + @Schema(description = "랜덤 불꽃이 메세지") + private String randomMessage; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java b/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java index 3af67f29..f461824b 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/helper/DonationHelper.java @@ -1,10 +1,22 @@ package com.example.matchapi.donation.helper; import com.example.matchcommon.annotation.Helper; +import com.example.matchdomain.common.model.MessageType; +import com.example.matchdomain.common.model.RandomMessage; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.enums.DonationStatus; import lombok.RequiredArgsConstructor; import java.text.DecimalFormat; +import java.time.Duration; import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +import static com.example.matchdomain.common.model.MessageType.*; +import static com.example.matchdomain.donation.entity.enums.RegularStatus.ONE_TIME; @Helper @RequiredArgsConstructor @@ -14,21 +26,48 @@ public String parsePriceComma(int amount){ return decimalFormat.format(amount)+"원"; } - - public String timeFormat(LocalDateTime createdAt) { - return createdAt.getYear() + "." + createdAt.getMonthValue() + "." + createdAt.getDayOfMonth(); + public String createRandomMessage(DonationUser donationUser) { + switch(donationUser.getDonationStatus()){ + case EXECUTION_BEFORE: + if(!isOneDayPassed(donationUser.getCreatedAt())){ + return getRandomMessageType(PAY_SUCCESS); + } + else{ + return getRandomMessageType(PAY_ONE_DAY); + } + case EXECUTION_UNDER: + return getRandomMessageType(UNDER); + case EXECUTION_SUCCESS: + return getRandomMessageType(COMPLETE); + } + return "하이"; } - public String dayTimeFormat(LocalDateTime createdAt) { - return createdAt.getYear() + "." + createdAt.getMonthValue() + "." + createdAt.getDayOfMonth() + " " + checkTimes(createdAt.getHour()) + ":" + checkTimes(createdAt.getMinute()); - } + public int getDonationSequence(DonationUser donationUser, Long donationId) { - public String checkTimes(int time){ - if(time < 10){ - return "0" + time; + if(donationUser.getRegularStatus() == ONE_TIME){ + return 0; } else{ - return String.valueOf(time); + return donationIdLists(donationUser.getRegularPayment().getDonationUser()).indexOf(donationId) + 1; } } + + public List donationIdLists(List donationUser) { + return donationUser.stream().map(DonationUser::getId).collect(Collectors.toList()); + } + + + public String getRandomMessageType(MessageType messageType) { + List paySuccessMessages = Arrays.stream(RandomMessage.values()) + .filter(rm -> rm.getMessageType() == messageType) + .collect(Collectors.toList()); + + Random random = new Random(); + return paySuccessMessages.get(random.nextInt(paySuccessMessages.size())).getMessage(); + } + + public boolean isOneDayPassed(LocalDateTime dateTime) { + return Duration.between(dateTime, LocalDateTime.now()).toDays() >= 1; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationHistoryService.java b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationHistoryService.java new file mode 100644 index 00000000..9c4f8711 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationHistoryService.java @@ -0,0 +1,31 @@ +package com.example.matchapi.donation.service; + +import com.example.matchapi.donation.convertor.DonationConvertor; +import com.example.matchdomain.donation.adaptor.DonationHistoryAdaptor; +import com.example.matchdomain.donation.entity.DonationHistory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import static com.example.matchdomain.donation.entity.enums.HistoryStatus.CREATE; +import static com.example.matchdomain.donation.entity.enums.HistoryStatus.TURN_ON; + +@Service +@RequiredArgsConstructor +public class DonationHistoryService { + private final DonationHistoryAdaptor donationHistoryAdaptor; + private final DonationConvertor donationConvertor; + + public void postRegularDonationHistory(Long regularPaymentId, Long donationId) { + saveDonationHistory(donationConvertor.convertToDonationHistoryTurnOn(regularPaymentId, TURN_ON)); + + saveDonationHistory(donationConvertor.convertToDonationHistory(donationId, CREATE)); + } + + public void saveDonationHistory(DonationHistory donationHistory){ + donationHistoryAdaptor.saveDonationHistory(donationHistory); + } + + public void oneTimeDonationHistory(Long donationId){ + saveDonationHistory(donationConvertor.convertToDonationHistory(donationId, CREATE)); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java index bff86a74..4534b9a6 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java @@ -1,309 +1,153 @@ package com.example.matchapi.donation.service; import com.example.matchapi.donation.convertor.DonationConvertor; +import com.example.matchapi.donation.convertor.RegularPaymentConvertor; import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; -import com.example.matchapi.order.service.OrderService; +import com.example.matchapi.portone.dto.PaymentReq; import com.example.matchapi.portone.service.PaymentService; -import com.example.matchapi.project.convertor.ProjectConvertor; import com.example.matchapi.project.dto.ProjectRes; import com.example.matchcommon.exception.BadRequestException; -import com.example.matchcommon.exception.NotFoundException; import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.adaptor.DonationHistoryAdaptor; +import com.example.matchdomain.donation.adaptor.RegularPaymentAdaptor; import com.example.matchdomain.donation.entity.*; -import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.enums.HistoryStatus; -import com.example.matchdomain.donation.repository.DonationHistoryRepository; import com.example.matchdomain.donation.repository.DonationUserRepository; import com.example.matchdomain.donation.repository.RegularPaymentRepository; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.user.entity.User; +import com.siot.IamportRestClient.response.Payment; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import javax.transaction.Transactional; -import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.temporal.TemporalAdjusters; -import java.util.ArrayList; +import java.time.format.DateTimeFormatter; import java.util.List; -import static com.example.matchcommon.constants.MatchStatic.FIRST_TIME; -import static com.example.matchcommon.constants.MatchStatic.LAST_TIME; import static com.example.matchdomain.common.model.Status.ACTIVE; import static com.example.matchdomain.donation.entity.enums.DonationStatus.*; -import static com.example.matchdomain.donation.entity.enums.HistoryStatus.CREATE; +import static com.example.matchdomain.donation.entity.enums.RegularPayStatus.PROCEEDING; import static com.example.matchdomain.donation.entity.enums.RegularPayStatus.USER_CANCEL; import static com.example.matchdomain.donation.exception.CancelRegularPayErrorCode.REGULAR_PAY_NOT_CORRECT_USER; -import static com.example.matchdomain.donation.exception.CancelRegularPayErrorCode.REGULAR_PAY_NOT_EXIST; -import static com.example.matchdomain.donation.exception.DonationListErrorCode.FILTER_NOT_EXIST; +import static com.example.matchdomain.donation.exception.CancelRegularPayErrorCode.REGULAR_PAY_NOT_STATUS; import static com.example.matchdomain.donation.exception.DonationRefundErrorCode.*; -import static com.example.matchdomain.donation.exception.GetRegularErrorCode.REGULAR_NOT_EXIST; -import static com.example.matchdomain.project.entity.enums.ImageRepresentStatus.REPRESENT; @Service @RequiredArgsConstructor public class DonationService { - private final DonationUserRepository donationUserRepository; - private final OrderService orderService; private final DonationConvertor donationConvertor; private final RegularPaymentRepository regularPaymentRepository; - private final DonationHelper donationHelper; - private final ProjectConvertor projectConvertor; - private final DonationHistoryRepository donationHistoryRepository; private final PaymentService paymentService; - - public PageResponse> getDonationList(Long userId, int filter, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - Page donationUsers = null; - - List donationLists = new ArrayList<>(); - - if(filter == 0){ - donationUsers = donationUserRepository.findByUserIdAndStatusAndDonationStatusNotOrderByCreatedAtDesc(userId, ACTIVE,EXECUTION_REFUND, pageable); - } - else if(filter == 1){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(userId,DonationStatus.EXECUTION_SUCCESS, ACTIVE, pageable); - } - else if(filter == 2){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(userId,DonationStatus.EXECUTION_SUCCESS, ACTIVE, pageable); - - }else if(filter == 3){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(userId, DonationStatus.EXECUTION_SUCCESS, ACTIVE, pageable); - - }else{ - throw new BadRequestException(FILTER_NOT_EXIST); - } - - donationUsers.getContent().forEach( - result ->{ - donationLists.add( - donationConvertor.DonationList(result) - ); - } - ); + private final RegularPaymentAdaptor regularPaymentAdaptor; + private final DonationAdaptor donationAdaptor; + private final DonationHistoryAdaptor donationHistoryAdaptor; + private final RegularPaymentConvertor regularPaymentConvertor; + private final DonationHelper donationHelper; + public PageResponse> getDonationList(Long userId, int filter, int page, int size) { + Page donationUsers = donationAdaptor.findDonationList(userId, filter, page ,size); - return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationLists); + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationConvertor.convertToDonationList(donationUsers)); } @Transactional public void refundDonation(User user, Long donationId) { - DonationUser donationUser = donationUserRepository.findById(donationId).orElseThrow(() -> new NotFoundException(DONATION_NOT_EXIST)); + DonationUser donationUser = donationAdaptor.findById(donationId); + if(!donationUser.getUserId().equals(user.getId())) throw new BadRequestException(DONATION_NOT_CORRECT_USER); + if(!donationUser.getDonationStatus().equals(EXECUTION_BEFORE)) throw new BadRequestException(CANNOT_DELETE_DONATION_STATUS); + paymentService.refundPayment(donationUser.getTid()); - donationUser.setDonationStatus(EXECUTION_REFUND); - } - //불꽃이 필터링 0 = 불꽃이 전체, 1 = 전달 전 불꽃이, 2 = 절달 중인 불꽃이, 3 = 전달 완료된 불꽃이 - //정렬 필터링 0 = 최신순, 1 = 오래된 순, 2 = 기부금액 큰 순, 3 = 기부금액 작은 순 - public PageResponse> getFlameList(User user, int page, int size, int flame, int order, String content) { - Pageable pageable = PageRequest.of(page, size); - - Page donationUsers = null; - - List flameLists = new ArrayList<>(); - - if(flame == 0){ - if(content == null){ - if(order == 0 ){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotAndStatusOrderByCreatedAtDesc(user.getId(), EXECUTION_REFUND, ACTIVE, pageable); - } - else if(order == 1){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotAndStatusOrderByCreatedAtAsc(user.getId(),EXECUTION_REFUND, ACTIVE, pageable); - } - else if(order == 2){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotAndStatusOrderByPriceDesc(user.getId(), EXECUTION_REFUND, ACTIVE, pageable); - } - else if(order ==3){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotAndStatusOrderByPriceAsc(user.getId(), EXECUTION_REFUND, ACTIVE, pageable); - } - } - else{ - if(order == 0 ){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtDesc(user.getId(), EXECUTION_REFUND, content, content, content, ACTIVE, pageable); - } - else if(order == 1){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtAsc(user.getId(), EXECUTION_REFUND, content, content, content, ACTIVE, pageable); - } - else if(order == 2){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceDesc(user.getId(), EXECUTION_REFUND, content, content, content, ACTIVE, pageable); - } - else if(order ==3){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusNotOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceAsc(user.getId(), EXECUTION_REFUND, content, content, content, ACTIVE, pageable); - } - } - } - else { - DonationStatus donationStatus = null; - if(flame == 1){ - donationStatus = EXECUTION_BEFORE; - } - else if(flame ==2){ - donationStatus = EXECUTION_UNDER; - } - else{ - donationStatus = EXECUTION_SUCCESS; - } - - if(content == null){ - if(order == 0 ){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(user.getId(), donationStatus, ACTIVE, pageable); - } - else if(order == 1){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtAsc(user.getId(),donationStatus, ACTIVE,pageable); - } - else if(order == 2){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByPriceDesc(user.getId(), donationStatus, ACTIVE, pageable); - } - else if(order ==3){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByPriceAsc(user.getId(), donationStatus, ACTIVE, pageable); - } - } - else{ - if(order == 0 ){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtDesc(user.getId(), donationStatus, content, content, content, ACTIVE, pageable); - } - else if(order == 1){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByCreatedAtAsc(user.getId(), donationStatus, content, content, content, ACTIVE, pageable); - } - else if(order == 2){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceDesc(user.getId(), donationStatus, content, content, content, ACTIVE, pageable); - } - else if(order ==3){ - donationUsers = donationUserRepository.findByUserIdAndDonationStatusOrProject_UsagesContainingOrProject_ProjectNameContainingOrProject_ProjectExplanationContainingAndStatusOrderByPriceAsc(user.getId(), donationStatus, content, content, content, ACTIVE, pageable); - } - } - } - - donationUsers.getContent().forEach( - result -> flameLists.add( - donationConvertor.Flame(result) - ) - ); - - - return new PageResponse<>(donationUsers.isLast(),donationUsers.getTotalElements(),flameLists); + donationUser.setDonationStatus(EXECUTION_REFUND); } public void cancelRegularPay(User user, Long regularId) { - RegularPayment regularPayment = regularPaymentRepository.findByIdAndStatus(regularId, ACTIVE).orElseThrow(() -> new BadRequestException(REGULAR_PAY_NOT_EXIST)); + RegularPayment regularPayment = regularPaymentAdaptor.findRegularPaymentByStatus(regularId, ACTIVE); if(!regularPayment.getUserId().equals(user.getId())) throw new BadRequestException(REGULAR_PAY_NOT_CORRECT_USER); - + if(!regularPayment.getRegularPayStatus().equals(PROCEEDING)) throw new BadRequestException(REGULAR_PAY_NOT_STATUS); regularPayment.setRegularPayStatus(USER_CANCEL); + regularPaymentRepository.save(regularPayment); } public DonationRes.DonationCount getDonationCount(User user) { - List donationUser = donationUserRepository.findByUserAndDonationStatusNotAndStatus(user, EXECUTION_REFUND, ACTIVE); + List donationUser = donationAdaptor.findByDonationCount(user); - return donationConvertor.DonationCount(donationUser); + return donationConvertor.convertToDonationCount(donationUser); } public PageResponse> getBurningMatch(User user, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - Page flameLists = donationUserRepository.getFlameList(user.getId(), pageable); - - List burningMatchRes = new ArrayList<>(); + Page flameLists = donationAdaptor.findFlameList(user, page, size); - flameLists.forEach( - result -> { - burningMatchRes.add(donationConvertor.BurningMatch(result)); - } - ); - - - return new PageResponse<>(flameLists.isLast(), flameLists.getTotalElements(), burningMatchRes); + return new PageResponse<>(flameLists.isLast(), flameLists.getTotalElements(), donationConvertor.BurningMatch(flameLists.getContent())); } @Transactional public DonationRes.DonationRegular getDonationRegular(Long regularPayId, User user) { - RegularPayment regularPayment = regularPaymentRepository.findById(regularPayId).orElseThrow(()-> new BadRequestException(REGULAR_NOT_EXIST)); - return donationConvertor.DonationRegular(regularPayment); + RegularPayment regularPayment = regularPaymentAdaptor.findById(regularPayId); + return donationConvertor.convertToDonationRegular(regularPayment); } @Transactional public PageResponse> getDonationRegularList(Long regularPayId, User user, int page, int size) { - System.out.println("존재 유무 확인"); - RegularPayment regularPayment = regularPaymentRepository.findById(regularPayId).orElseThrow(()-> new BadRequestException(REGULAR_NOT_EXIST)); - Pageable pageable = PageRequest.of(page, size); - System.out.println("페이지 네이션"); - Page donationHistories = donationHistoryRepository.findByDonationUser_RegularPaymentIdOrRegularPaymentIdOrProjectIdAndHistoryStatusNotOrderByCreatedAtAsc(regularPayId, regularPayId, regularPayment.getProjectId(), HistoryStatus.TURN_ON ,pageable); + RegularPayment regularPayment = regularPaymentAdaptor.findById(regularPayId); + Page donationHistories = donationHistoryAdaptor.findDonationRegularList(regularPayId, regularPayment.getProjectId(), HistoryStatus.TURN_ON ,page, size); - List donationRegularLists = new ArrayList<>(); - donationHistories.forEach( - result -> donationRegularLists.add( - donationConvertor.DonationRegularList(result) - ) - ); - - return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationRegularLists); + return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConvertor.convertToDonationRegularList(donationHistories.getContent(), "")); } public List getPayList(User user, Long regularPayId) { - List donationUsers = donationUserRepository.findByRegularPaymentIdAndStatusOrderByCreatedAtDesc(regularPayId, ACTIVE); - List payLists = new ArrayList<>(); - - donationUsers.forEach( - result -> payLists.add( - donationConvertor.PayList(result) - ) - ); + List donationUsers = donationAdaptor.findPayList(regularPayId); - return payLists; + return donationConvertor.convertToPayList(donationUsers); } public PageResponse> getFlameProjectList(User user, String content, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - Page donationUsers = donationUserRepository.findByUserAndInherenceNameContainingAndProject_ProjectImg_RepresentStatusOrderByCreatedAtDesc(user, content, REPRESENT, pageable); - List flameProjectLists = new ArrayList<>(); - - donationUsers.getContent().forEach( - result -> flameProjectLists.add( - donationConvertor.FlameProject(result) - ) - ); - return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), flameProjectLists); + Page donationUsers = donationAdaptor.getFlameProjectList(user, content, page, size); + + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationConvertor.convertToFlameProjectList(donationUsers.getContent())); } public PageResponse> getFlameRegularList(Long donationId, User user, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - DonationUser donationUser = donationUserRepository.findById(donationId).orElseThrow(()-> new BadRequestException(DONATION_NOT_EXIST)); - List donationRegularLists = new ArrayList<>(); - Page donationHistories = donationHistoryRepository.getDonationHistoryCustom(donationUser.getRegularPaymentId(), donationId, CREATE, pageable, donationUser.getProjectId()); - donationHistories.forEach( - result -> donationRegularLists.add( - donationConvertor.DonationRegularList(result) - ) - ); - - return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationRegularLists); + DonationUser donationUser = donationAdaptor.findById(donationId); + + Page donationHistories = donationHistoryAdaptor.findDonationHistory(donationUser, donationId, page, size); + + return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConvertor.convertToDonationRegularList(donationHistories.getContent(), donationUser.getInherenceName())); } public DonationRes.DonationFlame getFlameRegular(Long donationId, User user) { - DonationUser donationUser = donationUserRepository.findById(donationId).orElseThrow(()-> new BadRequestException(DONATION_NOT_EXIST)); - RegularPayment regularPayment = regularPaymentRepository.findById(donationUser.getRegularPaymentId()).orElseThrow(()-> new BadRequestException(REGULAR_NOT_EXIST)); - return donationConvertor.DonationFlame(regularPayment, donationUser); + DonationUser donationUser = donationAdaptor.findById(donationId); + int sequence = donationHelper.getDonationSequence(donationUser, donationId); + return donationConvertor.convertToDonationFlame(sequence, donationUser); } public PageResponse> getMatchHistory(User user, Long projectId, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - List matchHistories = new ArrayList<>(); - Page donationHistories = donationHistoryRepository.findByDonationUser_ProjectOrProjectIdIdOrderByCreatedAtAsc(projectId, pageable); + Page donationHistories = donationHistoryAdaptor.findMatchHistory(projectId, page, size); - donationHistories.forEach( - result -> matchHistories.add( - donationConvertor.MatchHistory(result) - ) - ); + return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), donationConvertor.convertToMatchHistory(donationHistories.getContent())); + } + public PageResponse> getUserMatchList(User user, int page, int size) { + Page regularPayments = regularPaymentAdaptor.findByUser(user, page, size); - return new PageResponse<>(donationHistories.isLast(), donationHistories.getTotalElements(), matchHistories); + return new PageResponse<>(regularPayments.isLast(), regularPayments.getTotalElements(), regularPaymentConvertor.convertToMatchList(regularPayments.getContent())); } + + @Cacheable(value = "flameCache", key = "{#user.id, #page, #size}") + public PageResponse> getBurningFlameList(User user, int page, int size) { + Page donationUsers = donationAdaptor.findByUser(user, page, size); + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), regularPaymentConvertor.convertToBurningFlameList(donationUsers.getContent())); + } + } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java index 6b15cad0..9fac9ded 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationTemporaryService.java @@ -31,14 +31,14 @@ public class DonationTemporaryService { private final DonationListRepository donationListRepository; private final DonationHelper donationHelper; public DonationTemporaryRes.UserInfo getUserInfo(User user) { - return donationTemporaryConvertor.UserInfo(user); + return donationTemporaryConvertor.convertToUserInfo(user); } public void postDonationTemporary(User user, DonationTemporaryReq.DonationInfo donationInfo) { if(donationInfo.getAlarmMethod() == AlarmMethod.SMS) { - donationTemporaryRepository.save(donationTemporaryConvertor.DonationInfo(user, donationInfo)); + donationTemporaryRepository.save(donationTemporaryConvertor.convertToDonationInfo(user, donationInfo)); }else{ - donationTemporaryRepository.save(donationTemporaryConvertor.DonationInfoEmail(user, donationInfo)); + donationTemporaryRepository.save(donationTemporaryConvertor.convertToDonationInfoEmail(user, donationInfo)); } } @@ -49,7 +49,7 @@ public PageResponse> getDonationList(Don donationLists.getContent().forEach( result -> donationList.add( - donationTemporaryConvertor.DonationList(result, donationHelper.parsePriceComma(result.getAmount())) + donationTemporaryConvertor.convertToDonationList(result, donationHelper.parsePriceComma(result.getAmount())) ) ); @@ -73,7 +73,7 @@ public PageResponse> getDona donationTemporaries = donationTemporaryRepository.findByNameContainingAndDepositOrderByCreatedAtDesc(content,deposit, pageable); } } - return new PageResponse<>(donationTemporaries.isLast(), donationTemporaries.getTotalElements(), donationTemporaryConvertor.DonationRequestAdminList(donationTemporaries.getContent())); + return new PageResponse<>(donationTemporaries.isLast(), donationTemporaries.getTotalElements(), donationTemporaryConvertor.convertToDonationRequestAdminList(donationTemporaries.getContent())); } @Transactional @@ -81,7 +81,7 @@ public void postDonationDeposit(DonationTemporaryReq.DonationDeposit donationDep DonationTemporary donationTemporary = donationTemporaryRepository.findById(donationDeposit.getDonationRequestId()) .orElseThrow(()-> new BadRequestException(NOT_EXIST_DONATION_REQUEST)); - donationListRepository.save(donationTemporaryConvertor.DonationDeposit(donationDeposit)); + donationListRepository.save(donationTemporaryConvertor.convertToDonationDeposit(donationDeposit)); donationTemporary.setDeposit(Deposit.EXISTENCE); donationTemporaryRepository.save(donationTemporary); @@ -90,6 +90,6 @@ public void postDonationDeposit(DonationTemporaryReq.DonationDeposit donationDep public DonationTemporaryRes.DonationDetail getDonationInfo(Long donationRequestId) { DonationTemporary donationTemporary = donationTemporaryRepository.findById(donationRequestId) .orElseThrow(()-> new BadRequestException(NOT_EXIST_DONATION_REQUEST)); - return donationTemporaryConvertor.DonationInfoDetail(donationTemporary); + return donationTemporaryConvertor.convertToDonationInfoDetail(donationTemporary); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/event/controller/EventController.java b/Match-Api/src/main/java/com/example/matchapi/event/controller/EventController.java new file mode 100644 index 00000000..b4a3f2cb --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/event/controller/EventController.java @@ -0,0 +1,43 @@ +package com.example.matchapi.event.controller; + +import com.example.matchapi.event.dto.EventRes; +import com.example.matchapi.event.service.EventService; +import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.event.exception.GetEventErrorCode; +import com.example.matchdomain.user.exception.UserAuthErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/events") +@RequiredArgsConstructor +@Tag(name = "12-Event 🎉 이벤트 관련 API", description = "이벤트 관련 API 입니다.") +public class EventController { + private final EventService eventService; + + @GetMapping("") + @Operation(summary = "12-01 이벤트 리스트 조회") + @ApiErrorCodeExample(UserAuthErrorCode.class) + public CommonResponse>> getEventLists( + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "10") int size + ){ + return CommonResponse.onSuccess(eventService.getEventList(page, size)); + } + + @GetMapping("/{eventId}") + @Operation(summary = "12-02 이벤트 상세 조회") + @ApiErrorCodeExample({GetEventErrorCode.class, UserAuthErrorCode.class}) + public CommonResponse getEventDetail(@PathVariable Long eventId){ + return CommonResponse.onSuccess(eventService.getEventDetail(eventId)); + } + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/event/convetor/EventConvertor.java b/Match-Api/src/main/java/com/example/matchapi/event/convetor/EventConvertor.java new file mode 100644 index 00000000..96baa480 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/event/convetor/EventConvertor.java @@ -0,0 +1,99 @@ +package com.example.matchapi.event.convetor; + +import com.example.matchapi.admin.event.dto.EventUploadReq; +import com.example.matchapi.common.util.TimeHelper; +import com.example.matchapi.event.dto.EventRes; +import com.example.matchapi.notice.dto.NoticeRes; +import com.example.matchcommon.annotation.Convertor; +import com.example.matchdomain.common.model.ContentsType; +import com.example.matchdomain.event.entity.Event; +import com.example.matchdomain.event.entity.EventContent; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Convertor +@RequiredArgsConstructor +public class EventConvertor { + private final TimeHelper timeHelper; + public List convertToEventList(List content) { + List eventLists = new ArrayList<>(); + + content.forEach( + result -> { + eventLists.add(convertToEventListDetail(result)); + } + ); + return eventLists; + } + + private EventRes.EventList convertToEventListDetail(Event result) { + return EventRes.EventList + .builder() + .eventId(result.getId()) + .title(result.getTitle()) + .thumbnail(result.getThumbnail()) + .smallTitle(result.getSmallTitle()) + .eventStatus(timeHelper.checkFinishStatus(result.getEventEndDate())) + .startDate(result.getEventStartDate()) + .endDate(result.getEventEndDate()) + .build(); + } + + public EventRes.EventDetail convertToEventDetail(Event event) { + EventRes.EventInfo eventInfo = convertToEventInfo(event); + List eventContents = new ArrayList<>(); + + event.getEventContents().forEach( + result -> eventContents.add(convertToEventContents(result)) + ); + + return EventRes.EventDetail + .builder() + .eventInfo(eventInfo) + .eventContents(eventContents) + .build(); + } + + private EventRes.EventContents convertToEventContents(EventContent result) { + return EventRes.EventContents + .builder() + .contentId(result.getId()) + .contentsType(result.getContentsType()) + .contents(result.getContents()) + .build(); + } + + private EventRes.EventInfo convertToEventInfo(Event event) { + return EventRes.EventInfo + .builder() + .eventId(event.getId()) + .title(event.getTitle()) + .smallTitle(event.getSmallTitle()) + .startDate(event.getEventStartDate()) + .endDate(event.getEventEndDate()) + .build(); + } + + public Event convertToEventUpload(EventUploadReq eventUploadReq, String thumbnail) { + return Event + .builder() + .title(eventUploadReq.getTitle()) + .eventType(eventUploadReq.getEventType()) + .smallTitle(eventUploadReq.getSmallTitle()) + .thumbnail(thumbnail) + .eventStartDate(eventUploadReq.getEventStartDate()) + .eventEndDate(eventUploadReq.getEventEndDate()) + .build(); + } + + public EventContent convertToEventContents(Long eventId, String contents, ContentsType contentsType) { + return EventContent + .builder() + .eventId(eventId) + .contentsType(contentsType) + .contents(contents) + .build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/event/dto/EventRes.java b/Match-Api/src/main/java/com/example/matchapi/event/dto/EventRes.java new file mode 100644 index 00000000..95e13734 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/event/dto/EventRes.java @@ -0,0 +1,76 @@ +package com.example.matchapi.event.dto; + +import com.example.matchdomain.common.model.ContentsType; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.*; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.time.LocalDate; +import java.util.List; + +public class EventRes { + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class EventList implements Serializable { + private Long eventId; + + private String thumbnail; + + private String title; + + private String smallTitle; + + private String eventStatus; + + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate startDate; + + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate endDate; + } + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class EventDetail { + private EventInfo eventInfo; + + private List eventContents; + } + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class EventInfo{ + private Long eventId; + + private String title; + + private String smallTitle; + + + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate startDate; + + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate endDate; + } + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class EventContents{ + private Long contentId; + + private ContentsType contentsType; + + private String contents; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/event/service/EventService.java b/Match-Api/src/main/java/com/example/matchapi/event/service/EventService.java new file mode 100644 index 00000000..4d46fb27 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/event/service/EventService.java @@ -0,0 +1,38 @@ +package com.example.matchapi.event.service; + +import com.example.matchapi.event.convetor.EventConvertor; +import com.example.matchapi.event.dto.EventRes; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.event.adaptor.EventAdaptor; +import com.example.matchdomain.event.entity.Event; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class EventService { + private final EventConvertor eventConvertor; + private final EventAdaptor eventAdaptor; + + @Cacheable(value = "eventCache", key = "{#page, #size}") + public PageResponse> getEventList(int page, int size) { + Page events = eventAdaptor.findEvent(page, size); + List eventLists = cachingEventLists(events.getContent()); + return new PageResponse<>(events.isLast(), events.getTotalElements(), eventLists); + } + + public List cachingEventLists(List content) { + return eventConvertor.convertToEventList(content); + } + + + public EventRes.EventDetail getEventDetail(Long eventId) { + Event event = eventAdaptor.findByEvent(eventId); + return eventConvertor.convertToEventDetail(event); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/keword/controller/keywordController.java b/Match-Api/src/main/java/com/example/matchapi/keword/controller/keywordController.java new file mode 100644 index 00000000..d36c226c --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/keword/controller/keywordController.java @@ -0,0 +1,27 @@ +package com.example.matchapi.keword.controller; + +import com.example.matchapi.keword.dto.KeywordRes; +import com.example.matchapi.keword.service.KeywordService; +import com.example.matchcommon.reponse.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/keywords") +@Tag(name = "09-Keyword 추천 검색 키워드 🔎",description = "검색 키워드 API") +public class keywordController { + private final KeywordService keywordService; + + @GetMapping("") + @Operation(summary = "09-01 Keyword 추천 키워드 조회💸") + public CommonResponse> getKeywordList(){ + return CommonResponse.onSuccess(keywordService.getKeywordList()); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/keword/convertor/KeywordConvertor.java b/Match-Api/src/main/java/com/example/matchapi/keword/convertor/KeywordConvertor.java new file mode 100644 index 00000000..1721983c --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/keword/convertor/KeywordConvertor.java @@ -0,0 +1,18 @@ +package com.example.matchapi.keword.convertor; + +import com.example.matchapi.keword.dto.KeywordRes; +import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchdomain.keyword.entity.SearchKeyword; + +@Convertor +public class KeywordConvertor { + public KeywordRes.KeywordList convertToKeywordList(SearchKeyword result) { + return KeywordRes.KeywordList + .builder() + .keywordId(result.getId()) + .keyword(result.getKeyword()) + .priority(result.getPriority()) + .build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/keword/dto/KeywordRes.java b/Match-Api/src/main/java/com/example/matchapi/keword/dto/KeywordRes.java new file mode 100644 index 00000000..de174255 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/keword/dto/KeywordRes.java @@ -0,0 +1,24 @@ +package com.example.matchapi.keword.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.io.Serializable; + +public class KeywordRes { + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class KeywordList implements Serializable { + private static final long serialVersionUID = 12312312421412L; + + private Long keywordId; + + @Schema(name = "priority", description = "우선 순위 입니다.") + private int priority; + + private String keyword; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/keword/service/KeywordService.java b/Match-Api/src/main/java/com/example/matchapi/keword/service/KeywordService.java new file mode 100644 index 00000000..63021870 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/keword/service/KeywordService.java @@ -0,0 +1,38 @@ +package com.example.matchapi.keword.service; + +import com.example.matchapi.admin.keyword.dto.AdminKeywordReq; +import com.example.matchapi.keword.convertor.KeywordConvertor; +import com.example.matchapi.keword.dto.KeywordRes; +import com.example.matchdomain.keyword.entity.SearchKeyword; +import com.example.matchdomain.keyword.repository.SearchKeywordRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class KeywordService { + private final KeywordConvertor keywordConvertor; + private final SearchKeywordRepository searchKeywordRepository; + + + @Cacheable(cacheNames = "keywordList", key = "'all'") + public List getKeywordList() { + return getKeyword(); + } + + public List getKeyword(){ + List searchKeywords = searchKeywordRepository.findAllByOrderByPriorityAsc(); + List keywordLists = new ArrayList<>(); + + searchKeywords.forEach( + result -> keywordLists.add(keywordConvertor.convertToKeywordList(result)) + ); + return keywordLists; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notice/controller/NoticeController.java b/Match-Api/src/main/java/com/example/matchapi/notice/controller/NoticeController.java new file mode 100644 index 00000000..dfd43190 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notice/controller/NoticeController.java @@ -0,0 +1,43 @@ +package com.example.matchapi.notice.controller; + +import com.example.matchapi.notice.dto.NoticeRes; +import com.example.matchapi.notice.service.NoticeService; +import com.example.matchapi.notification.dto.NotificationRes; +import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.notice.exception.GetNoticeErrorCode; +import com.example.matchdomain.user.entity.User; +import com.example.matchdomain.user.exception.UserAuthErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/notices") +@Tag(name = "11-Notice📝 공지사항 API", description = "공지사항 관련 API 입니다.") +public class NoticeController { + private final NoticeService noticeService; + + @Operation(summary = "11-01 공지사항 리스트 조회") + @GetMapping("") + public CommonResponse>> getNotificationList( + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "10") int size + ){ + return CommonResponse.onSuccess(noticeService.getNoticeList(page, size)); + } + + @Operation(summary = "11-02 공지사항 상세 조회") + @GetMapping("/{noticeId}") + @ApiErrorCodeExample({GetNoticeErrorCode.class, UserAuthErrorCode.class}) + public CommonResponse getNoticeDetail(@PathVariable Long noticeId){ + return CommonResponse.onSuccess(noticeService.getNoticeDetail(noticeId)); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notice/convertor/NoticeConvertor.java b/Match-Api/src/main/java/com/example/matchapi/notice/convertor/NoticeConvertor.java new file mode 100644 index 00000000..37895de3 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notice/convertor/NoticeConvertor.java @@ -0,0 +1,63 @@ +package com.example.matchapi.notice.convertor; + +import com.example.matchapi.common.util.TimeHelper; +import com.example.matchapi.notice.dto.NoticeRes; +import com.example.matchcommon.annotation.Convertor; +import com.example.matchdomain.notice.entity.Notice; +import com.example.matchdomain.notice.entity.NoticeContent; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Convertor +@RequiredArgsConstructor +public class NoticeConvertor { + private final TimeHelper timeHelper; + public List convertToNoticeList(List content) { + List noticeLists = new ArrayList<>(); + + content.forEach( + result -> noticeLists.add( + convertToNoticeListDetail(result) + ) + ); + + return noticeLists; + } + + private NoticeRes.NoticeList convertToNoticeListDetail(Notice result) { + return NoticeRes.NoticeList + .builder() + .noticeId(result.getId()) + .title(result.getTitle()) + .noticeType(result.getNoticeType().getType()) + .noticeDate(timeHelper.matchTimeFormat(result.getCreatedAt())) + .build(); + } + + public NoticeRes.NoticeDetail convertToNoticeDetail(Notice notice) { + NoticeRes.NoticeList noticeInfo = convertToNoticeListDetail(notice); + List noticeContents = new ArrayList<>(); + + notice.getNoticeContents().forEach( + result -> noticeContents.add( + convertToNoticeContentsDetail(result) + ) + ); + return NoticeRes.NoticeDetail + .builder() + .noticeInfo(noticeInfo) + .noticeContents(noticeContents) + .build(); + } + + private NoticeRes.NoticeContents convertToNoticeContentsDetail(NoticeContent result) { + return NoticeRes.NoticeContents + .builder() + .contentId(result.getId()) + .contentsType(result.getContentsType()) + .contents(result.getContents()) + .build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notice/dto/NoticeRes.java b/Match-Api/src/main/java/com/example/matchapi/notice/dto/NoticeRes.java new file mode 100644 index 00000000..4ce90e66 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notice/dto/NoticeRes.java @@ -0,0 +1,48 @@ +package com.example.matchapi.notice.dto; + +import com.example.matchdomain.common.model.ContentsType; +import lombok.*; + +import java.io.Serializable; +import java.util.List; + +public class NoticeRes { + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class NoticeList implements Serializable { + private Long noticeId; + + private String noticeType; + + private String title; + + private String noticeDate; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class NoticeDetail { + private NoticeList noticeInfo; + + private List noticeContents; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class NoticeContents{ + private Long contentId; + + private ContentsType contentsType; + + private String contents; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notice/service/NoticeService.java b/Match-Api/src/main/java/com/example/matchapi/notice/service/NoticeService.java new file mode 100644 index 00000000..d9b34eb7 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notice/service/NoticeService.java @@ -0,0 +1,32 @@ +package com.example.matchapi.notice.service; + +import com.example.matchapi.notice.convertor.NoticeConvertor; +import com.example.matchapi.notice.dto.NoticeRes; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.notice.adaptor.NoticeAdapter; +import com.example.matchdomain.notice.entity.Notice; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NoticeService { + private final NoticeConvertor noticeConvertor; + private final NoticeAdapter noticeAdapter; + + + @Cacheable(value = "noticeCache", key = "{#page, #size}") + public PageResponse> getNoticeList(int page, int size) { + Page notices = noticeAdapter.getNoticeList(page, size); + return new PageResponse<>(notices.isLast(), notices.getTotalElements(), noticeConvertor.convertToNoticeList(notices.getContent())); + } + + public NoticeRes.NoticeDetail getNoticeDetail(Long noticeId) { + Notice notice = noticeAdapter.findNoticeDetail(noticeId); + return noticeConvertor.convertToNoticeDetail(notice); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notification/controller/NotificationController.java b/Match-Api/src/main/java/com/example/matchapi/notification/controller/NotificationController.java new file mode 100644 index 00000000..5bb59e11 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notification/controller/NotificationController.java @@ -0,0 +1,44 @@ +package com.example.matchapi.notification.controller; + +import com.example.matchapi.notification.dto.NotificationRes; +import com.example.matchapi.notification.service.NotificationService; +import com.example.matchcommon.annotation.ApiErrorCodeExample; +import com.example.matchcommon.reponse.CommonResponse; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.notification.exception.GetNotificationErrorCode; +import com.example.matchdomain.user.entity.User; +import com.example.matchdomain.user.exception.UserAuthErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/notifications") +@Tag(name = "10-Notification🔔 알림", description = "알림 관련 API 입니다.") +public class NotificationController { + private final NotificationService notificationService; + @ApiErrorCodeExample(UserAuthErrorCode.class) + @Operation(summary = "10-01 알림 리스트 조회") + @GetMapping("") + public CommonResponse> getNotificationList( + @AuthenticationPrincipal User user, + @Parameter(description = "페이지", example = "0") @RequestParam(required = false, defaultValue = "0") int page, + @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = false, defaultValue = "10") int size + ){ + return CommonResponse.onSuccess(notificationService.getNotificationList(user, page, size)); + } + + @ApiErrorCodeExample({UserAuthErrorCode.class, GetNotificationErrorCode.class}) + @Operation(summary = "10-02 알림 상세 조회") + @GetMapping("/{notificationId}") + public CommonResponse getNotificationDetail(@PathVariable Long notificationId){ + return CommonResponse.onSuccess(notificationService.getNotificationDetail(notificationId)); + } + +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notification/convertor/NotificationConvertor.java b/Match-Api/src/main/java/com/example/matchapi/notification/convertor/NotificationConvertor.java new file mode 100644 index 00000000..ef7c2c91 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notification/convertor/NotificationConvertor.java @@ -0,0 +1,65 @@ +package com.example.matchapi.notification.convertor; + +import com.example.matchapi.common.util.TimeHelper; +import com.example.matchapi.notification.dto.NotificationRes; +import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.notification.entity.Notification; +import com.example.matchdomain.notification.enums.NotificationType; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; + +import java.util.ArrayList; +import java.util.List; + +@Convertor +@RequiredArgsConstructor +public class NotificationConvertor { + private final TimeHelper timeHelper; + public List convertToNotificationList(List notifications) { + List notificationLists = new ArrayList<>(); + + notifications.forEach( + result -> notificationLists.add( + convertToNotificationDetail(result) + ) + ); + + return notificationLists; + } + + private NotificationRes.NotificationList convertToNotificationDetail(Notification result) { + + return NotificationRes.NotificationList + .builder() + .notificationId(result.getId()) + .notificationType(result.getNotificationType().getType()) + .title(result.getTitle()) + .notificationDate(timeHelper.matchTimeFormat(result.getCreatedAt())) + .isRead(result.getIsRead()) + .build(); + } + + public Notification convertToNotificationTest(User user, FCMNotificationRequestDto fcmNotificationRequestDto) { + return Notification + .builder() + .body(fcmNotificationRequestDto.getBody()) + .notificationType(NotificationType.TEST) + .userId(user.getId()) + .title(fcmNotificationRequestDto.getTitle()) + .build(); + } + + public NotificationRes.NotificationDetail convertNotificationDetail(Notification notification) { + return NotificationRes.NotificationDetail + .builder() + .notificationId(notification.getId()) + .notificationType(notification.getNotificationType().getType()) + .title(notification.getTitle()) + .body(notification.getBody()) + .notificationDate(timeHelper.matchTimeFormat(notification.getCreatedAt())) + .build(); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notification/dto/NotificationRes.java b/Match-Api/src/main/java/com/example/matchapi/notification/dto/NotificationRes.java new file mode 100644 index 00000000..14eae15f --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notification/dto/NotificationRes.java @@ -0,0 +1,54 @@ +package com.example.matchapi.notification.dto; + +import lombok.*; + +import java.util.List; + +public class NotificationRes { + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class NotificationListInfo { + private int unReadCount; + + private List notificationLists; + } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class NotificationList{ + private Long notificationId; + + private String notificationType; + + private String title; + + private String notificationDate; + + private boolean isRead; + } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class NotificationDetail { + private Long notificationId; + + private String notificationType; + + private String title; + + private String body; + + private String notificationDate; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/notification/service/NotificationService.java b/Match-Api/src/main/java/com/example/matchapi/notification/service/NotificationService.java new file mode 100644 index 00000000..af5052d9 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/notification/service/NotificationService.java @@ -0,0 +1,39 @@ +package com.example.matchapi.notification.service; + +import com.example.matchapi.notification.convertor.NotificationConvertor; +import com.example.matchapi.notification.dto.NotificationRes; +import com.example.matchcommon.reponse.PageResponse; +import com.example.matchdomain.notification.adaptor.NotificationAdaptor; +import com.example.matchdomain.notification.entity.Notification; +import com.example.matchdomain.user.entity.User; +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NotificationService { + private final NotificationAdaptor notificationAdaptor; + private final NotificationConvertor notificationConvertor; + + public PageResponse getNotificationList(User user, int page, int size) { + Page notifications = notificationAdaptor.findByUser(user, page, size); + int notificationCount = notificationAdaptor.countByUnRead(user); + return new PageResponse<>(notifications.isLast(), notifications.getTotalElements(), new NotificationRes.NotificationListInfo(notificationCount, notificationConvertor.convertToNotificationList(notifications.getContent()))); + } + + public void saveTestNotification(User user, FCMNotificationRequestDto fcmNotificationRequestDto) { + Notification notification = notificationConvertor.convertToNotificationTest(user, fcmNotificationRequestDto); + + notificationAdaptor.saveNotification(notification); + } + + public NotificationRes.NotificationDetail getNotificationDetail(Long notificationId) { + Notification notification = notificationAdaptor.findNotification(notificationId); + notificationAdaptor.readNotification(notification); + return notificationConvertor.convertNotificationDetail(notification); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java b/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java index 80e8d060..b41232dc 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java @@ -3,6 +3,7 @@ import com.example.matchapi.common.aop.CheckIdExist; import com.example.matchapi.common.aop.CheckOneTimeProject; import com.example.matchapi.common.aop.CheckRegularProject; +import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.order.dto.OrderReq; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.service.OrderService; @@ -45,14 +46,11 @@ @Tag(name = "04-Order💸",description = "NicePayment 결제 API") public class OrderController { private final OrderService orderService; - private final NicePayProperties nicePayProperties; private final UserService userService; - @Value("${web.return.url}") - private String redirectUrl; @PostMapping("/{projectId}") @ApiErrorCodeExample(UserAuthErrorCode.class) - @Operation(summary= "04-00 Order💸 결제 요청용 처음 결제할 때 요청 보내기",description = "결제 요청용 API 입니다") + @Operation(summary= "04-00 Order💸 결제 요청용 처음 결제할 때 요청 보내기 Web Version",description = "결제 요청용 API 입니다") @CheckIdExist public CommonResponse requestPay( @Parameter(hidden = true) @AuthenticationPrincipal User user, @@ -62,17 +60,18 @@ public CommonResponse requestPay( return CommonResponse.onSuccess(orderId); } - - @PostMapping("/pay/{projectId}") - @ApiErrorCodeExample({OtherServerErrorCode.class, UserAuthErrorCode.class, RequestErrorCode.class, ProjectOneTimeErrorCode.class}) - @Operation(summary= "04-01 Order💸 단기 결제 API",description = "결제 API 입니다") - @CheckRegularProject - public CommonResponse requestPayment( + @PostMapping("/v2/{projectId}") + @ApiErrorCodeExample(UserAuthErrorCode.class) + @Operation(summary= "04-00 Order💸 결제 요청용 처음 결제할 때 요청 보내기 V2 flutter 인 경우 여기로 요청 보내주세요",description = "결제 요청용 API 입니다") + @CheckIdExist + public CommonResponse requestPayPrepare( @Parameter(hidden = true) @AuthenticationPrincipal User user, @Parameter(description = "프로젝트 ID", example = "1") @PathVariable("projectId") Long projectId, - @Valid @RequestBody OrderReq.OrderDetail orderDetail){ - log.info("04-03 Order 결제 인증용 API 결제 ID: " + orderDetail.getTid() + " 결제 금액 " + orderDetail.getAmount() +" 기부 프로젝트 ID : " + projectId); - return CommonResponse.onSuccess(orderService.requestPayment(user , orderDetail, projectId)); + @RequestParam int amount + ){ + log.info("결제 준비 요청 v2"); + String orderId = orderService.saveRequestPrepare(user, projectId, amount); + return CommonResponse.onSuccess(orderId); } @PostMapping("/pay/card") @@ -104,25 +103,23 @@ public CommonResponse deleteBillCard(@Parameter(hidden = true) @Authenti @PostMapping("/pay/card/{cardId}/{projectId}") @ApiErrorCodeExample({UserAuthErrorCode.class, OtherServerErrorCode.class, ProjectRegualrErrorCode.class, DeleteCardErrorCode.class}) @Operation(summary = "04-05 Order💸 정기 결제 등록 api #FRAME 결제 화면 - 정기 결제",description = "정기 결제 신청하기 API 입니다.") - public CommonResponse regularDonation( + public CommonResponse regularDonation( @Parameter(hidden = true) @AuthenticationPrincipal User user, @Parameter(description = "카드 id",example = "1") @PathVariable Long cardId, @Parameter(description = "프로젝트 id", example = "2") @PathVariable Long projectId, @Valid @RequestBody OrderReq.RegularDonation regularDonation){ - orderService.regularDonation(user, regularDonation, cardId, projectId); - return CommonResponse.onSuccess("정기 결제 등록 성공"); + return CommonResponse.onSuccess(orderService.regularDonation(user, regularDonation, cardId, projectId)); } @PostMapping("/pay/one/card/{cardId}/{projectId}") @ApiErrorCodeExample({UserAuthErrorCode.class, OtherServerErrorCode.class, ProjectOneTimeErrorCode.class, DeleteCardErrorCode.class}) @Operation(summary = "04-06 Order💸 빌키로 단기 결제 api #FRAME 결제 화면 - 단기 결제",description = "단 결제 신청하기 API 입니다.") - public CommonResponse oneTimeDonationCard( + public CommonResponse oneTimeDonationCard( @Parameter(hidden = true) @AuthenticationPrincipal User user, @Parameter(description = "카드 id",example = "1") @PathVariable Long cardId, @Parameter(description = "프로젝트 id", example = "2") @PathVariable Long projectId, @Valid @RequestBody OrderReq.OneTimeDonation oneTimeDonation){ - orderService.oneTimeDonationCard(user, oneTimeDonation, cardId, projectId); - return CommonResponse.onSuccess("단기 결제 성공"); + return CommonResponse.onSuccess(orderService.oneTimeDonationCard(user, oneTimeDonation, cardId, projectId)); } @PostMapping("/user") @@ -135,14 +132,14 @@ public CommonResponse getUserInfo( } - @DeleteMapping("/revoke/{cardId}") +/* @DeleteMapping("/revoke/{cardId}") @ApiErrorCodeExample({UserAuthErrorCode.class}) - @Operation(summary = "04-08 Order💸 간편결제 해지",description = "후원자 정보조회 API 입니다.") + @Operation(summary = "04-08 Order💸 간편결제 해지",description = "간편결제 해지 입니다.") public CommonResponse revokePay( @Parameter(hidden = true) @AuthenticationPrincipal User user, @PathVariable Long cardId ) { orderService.revokePay(user, cardId); return CommonResponse.onSuccess("간편 결제 해지"); - } + }*/ } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java b/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java index 84dd2092..5afc2130 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java @@ -1,102 +1,41 @@ package com.example.matchapi.order.convertor; +import com.example.matchapi.donation.helper.DonationHelper; import com.example.matchapi.order.dto.OrderReq; +import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.helper.OrderHelper; import com.example.matchapi.portone.dto.PaymentReq; import com.example.matchcommon.annotation.Convertor; import com.example.matchdomain.donation.entity.*; import com.example.matchdomain.donation.entity.enums.*; +import com.example.matchdomain.donation.entity.flameEnum.FlameImage; +import com.example.matchdomain.donation.entity.flameEnum.FlameType; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.redis.entity.OrderRequest; import com.example.matchinfrastructure.pay.nice.dto.*; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; -import com.siot.IamportRestClient.response.Payment; import lombok.RequiredArgsConstructor; +import java.util.ArrayList; +import java.util.List; + import static java.lang.Integer.parseInt; @Convertor @RequiredArgsConstructor public class OrderConvertor { private final OrderHelper orderHelper; - public DonationUser donationUser(NicePaymentAuth nicePaymentAuth, Long id, OrderReq.OrderDetail orderDetail, Long projectId, String flameName, String inherenceNumber) { - return DonationUser.builder() - .userId(id) - .projectId(projectId) - .price(orderDetail.getAmount()) - .tid(nicePaymentAuth.getTid()) - .orderId(nicePaymentAuth.getOrderId()) - .donationStatus(DonationStatus.EXECUTION_BEFORE) - .payMethod(orderHelper.getPayMethod(nicePaymentAuth.getPayMethod())) - .inherenceName(flameName) - .inherenceNumber(inherenceNumber) - .regularStatus(RegularStatus.ONE_TIME) - .build(); - } - public DonationUser donationUserV2(NicePaymentAuth nicePaymentAuth, Long id, Long amount, String projectId, String flameName, String inherenceNumber) { - return DonationUser.builder() - .userId(id) - .projectId(Long.valueOf(projectId)) - .price(amount) - .tid(nicePaymentAuth.getTid()) - .orderId(nicePaymentAuth.getOrderId()) - .donationStatus(DonationStatus.EXECUTION_BEFORE) - .payMethod(orderHelper.getPayMethod(nicePaymentAuth.getPayMethod())) - .inherenceName(flameName) - .inherenceNumber(inherenceNumber) - .regularStatus(RegularStatus.ONE_TIME) - .build(); - } - - public String createPlainText(OrderReq.RegistrationCard registrationCard) { - return "cardNo=" + registrationCard.getCardNo() + "&" - + "expYear=" + registrationCard.getExpYear() + "&" - + "expMonth=" + registrationCard.getExpMonth() + "&" - + "idNo=" + registrationCard.getIdNo() + "&" - + "cardPw=" + registrationCard.getCardPw(); - } + private final DonationHelper donationHelper; - public NiceBillOkRequest niceBillOk(NicePayBillkeyResponse nicePayBillkeyResponse, String orderId) { - return NiceBillOkRequest.builder() - .cardQuota(0) - .amount(10L) - .goodsName("카드 확인 용 결제") - .useShopInterest(false) - .orderId(orderId) - .build(); - } - - public RegularPayment RegularPayment(Long id, OrderReq.RegularDonation regularDonation, Long userCardId, Long projectId) { + public RegularPayment convertToRegularPayment(Long id, OrderReq.RegularDonation regularDonation, Long userCardId, Long projectId) { return RegularPayment.builder() .userId(id) .payDate(regularDonation.getPayDate()) .amount(regularDonation.getAmount()) .userCardId(userCardId) .projectId(projectId) - .build(); - } - - public UserCard UserCard(Long id, OrderReq.RegistrationCard registrationCard, NicePayBillkeyResponse nicePayBillkeyResponse){ - return UserCard.builder() - .userId(id) - .bid(nicePayBillkeyResponse.getBid()) - .cardNo(registrationCard.getCardNo()) - .expYear(registrationCard.getExpYear()) - .expMonth(registrationCard.getExpMonth()) - .idNo(registrationCard.getIdNo()) - .cardPw(registrationCard.getCardPw()) - .cardName(nicePayBillkeyResponse.getCardName()) - .orderId(nicePayBillkeyResponse.getOrderId()) - .build(); - } - - public RequestPaymentHistory recordHistory(Long userId, String orderId, String tid, Long amount, String reason) { - return RequestPaymentHistory.builder() - .userId(userId) - .tid(tid) - .orderId(orderId) - .amount(amount) - .reason(reason) + .regularPayStatus(RegularPayStatus.PROCEEDING) .build(); } @@ -105,53 +44,27 @@ public OrderRequest CreateRequest(Long userId, Long projectId, String orderId) { .userId(String.valueOf(userId)) .projectId(String.valueOf(projectId)) .orderId(orderId) - .ttl(2000L) + .ttl(10L) .build(); } - - public NiceBillOkRequest billCardOneTime(Long amount, String orderId) { - return NiceBillOkRequest.builder() - .cardQuota(0) - .amount(amount) - .goodsName("매치 기부금 결제") - .useShopInterest(false) - .orderId(orderId) - .build(); - } - - public DonationUser donationBillUser(NiceBillOkResponse niceBillOkResponse, Long id, Long amount, Long projectId, String flameName, String inherenceNumber, RegularStatus regularStatus, Long regularPaymentId) { - return DonationUser.builder() - .userId(id) - .projectId(projectId) - .price(amount) - .tid(niceBillOkResponse.getTid()) - .orderId(niceBillOkResponse.getOrderId()) - .donationStatus(DonationStatus.EXECUTION_BEFORE) - .payMethod(orderHelper.getPayMethod(niceBillOkResponse.getPayMethod())) - .inherenceName(flameName) - .inherenceNumber(inherenceNumber) - .regularStatus(regularStatus) - .regularPaymentId(regularPaymentId) - .build(); - } - - public DonationUser donationUserPortone(Payment payment, Long userId, PaymentReq.ValidatePayment validatePayment, Long projectId, String flameName, String inherenceNumber) { + public DonationUser convertToDonationUserPortone(Long userId, PaymentReq.ValidatePayment validatePayment, Long projectId, OrderRes.CreateInherenceDto createInherenceDto) { return DonationUser.builder() .userId(userId) .payMethod(orderHelper.getPayMethod(validatePayment.getPayMethod())) .projectId(projectId) .price((long) validatePayment.getAmount()) - .tid(payment.getImpUid()) - .orderId(payment.getMerchantUid()) + .tid(validatePayment.getImpUid()) + .orderId(validatePayment.getOrderId()) .donationStatus(DonationStatus.EXECUTION_BEFORE) - .payMethod(orderHelper.getPayMethod(payment.getPayMethod())) - .inherenceName(flameName) - .inherenceNumber(inherenceNumber) + .payMethod(orderHelper.getPayMethod(validatePayment.getPayMethod())) + .inherenceName(createInherenceDto.getInherenceName()) + .inherenceNumber(createInherenceDto.getInherenceNumber()) .regularStatus(RegularStatus.ONE_TIME) + .flameImage(FlameImage.NORMAL_IMG.getImg()) .build(); } - public UserCard UserBillCard(Long id, OrderReq.RegistrationCard registrationCard, PortOneBillResponse portOneBillResponse) { + public UserCard convertToUserBillCard(Long id, OrderReq.RegistrationCard registrationCard, PortOneBillResponse portOneBillResponse) { return UserCard.builder() .userId(id) .bid(portOneBillResponse.getCustomer_uid()) @@ -167,7 +80,7 @@ public UserCard UserBillCard(Long id, OrderReq.RegistrationCard registrationCard .build(); } - public DonationUser donationBillPayUser(PortOneBillPayResponse response, Long id, Long amount, Long projectId, String flameName, String inherenceNumber, RegularStatus regularStatus, Long regularPaymentId) { + public DonationUser donationBillPayUser(PortOneBillPayResponse response, Long id, Long amount, Long projectId, OrderRes.CreateInherenceDto createInherenceDto, RegularStatus regularStatus, Long regularPaymentId) { return DonationUser.builder() .userId(id) .projectId(projectId) @@ -176,10 +89,53 @@ public DonationUser donationBillPayUser(PortOneBillPayResponse response, Long id .orderId(response.getMerchant_uid()) .donationStatus(DonationStatus.EXECUTION_BEFORE) .payMethod(PayMethod.CARD) - .inherenceName(flameName) - .inherenceNumber(inherenceNumber) + .inherenceName(createInherenceDto.getInherenceName()) + .inherenceNumber(createInherenceDto.getInherenceNumber()) .regularStatus(regularStatus) .regularPaymentId(regularPaymentId) + .flameImage(FlameImage.NORMAL_IMG.getImg()) + .flameType(FlameType.NORMAL_FLAME) + .build(); + } + + public OrderRes.CompleteDonation convertToCompleteDonation(String name, Project project, Long amount) { + return OrderRes.CompleteDonation + .builder() + .username(name) + .title(project.getProjectName()) + .usages(project.getUsages()) + .amount(donationHelper.parsePriceComma(Math.toIntExact(amount))) + .regularStatus(project.getRegularStatus().getName()) + .build(); + } + + public List convertToUserCardLists(List userCards) { + List userBillCards = new ArrayList<>(); + + userCards.forEach( + result -> { + userBillCards.add( + new OrderRes.UserBillCard( + result.getId(), + result.getCardCode().getName(), + result.getCardName(), + orderHelper.maskMiddleNum(result.getCardNo()), + result.getCardAbleStatus().getName() + ) + ); + } + ); + return userBillCards; + } + + public OrderRequest convertToRequestPrepare(Long userId, Long projectId, int amount, String orderId) { + return OrderRequest + .builder() + .orderId(orderId) + .userId(String.valueOf(userId)) + .projectId(String.valueOf(projectId)) + .amount(amount) + .ttl(480L) .build(); } } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java index 41f37410..928292c4 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/dto/OrderRes.java @@ -1,5 +1,6 @@ package com.example.matchapi.order.dto; +import com.example.matchdomain.donation.entity.enums.RegularStatus; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; @@ -34,4 +35,34 @@ public static class UserDetail { private String phoneNumber; } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + @Schema(description = "기부 API API Response") + public static class CompleteDonation { + private String username; + + private String title; + + private String usages; + + private String amount; + + private String regularStatus; + } + + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class CreateInherenceDto { + private String inherenceName; + + private String inherenceNumber; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java b/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java index 48138d77..5f550f1b 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/helper/OrderHelper.java @@ -1,28 +1,29 @@ package com.example.matchapi.order.helper; +import com.example.matchapi.order.dto.OrderRes; import com.example.matchcommon.annotation.Helper; -import com.example.matchcommon.exception.BaseException; -import com.example.matchcommon.properties.NicePayProperties; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; +import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.PayMethod; import com.example.matchdomain.donation.entity.flameEnum.Adjective; import com.example.matchdomain.donation.entity.flameEnum.AdjectiveFlame; -import com.example.matchdomain.donation.repository.DonationUserRepository; -import com.example.matchinfrastructure.pay.nice.client.NiceAuthFeignClient; -import com.example.matchinfrastructure.pay.nice.dto.NicePayCancelRequest; +import com.example.matchdomain.user.entity.User; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; +import org.apache.commons.lang3.RandomStringUtils; -import java.util.Base64; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Random; +import java.util.UUID; +import java.util.stream.Collectors; import static com.example.matchcommon.constants.MatchStatic.BASIC; @Helper @RequiredArgsConstructor public class OrderHelper { - private final NicePayProperties nicePayProperties; - private final NiceAuthFeignClient niceAuthFeignClient; - private final DonationUserRepository donationUserRepository; + private final DonationAdaptor donationAdaptor; public PayMethod getPayMethod(String value) { for (PayMethod payMethod : PayMethod.values()) { @@ -33,27 +34,20 @@ public PayMethod getPayMethod(String value) { return null; } - public String getNicePaymentAuthorizationHeader() { - return BASIC + Base64.getEncoder().encodeToString((nicePayProperties.getClient() + ":" + nicePayProperties.getSecret()).getBytes()); + public List getInherenceName(List donationUsers){ + return donationUsers.stream() + .map(DonationUser :: getInherenceName).collect(Collectors.toList()); } - public void checkNicePaymentsResult(String resultCode, String resultMessage) { - switch(resultCode){ - case "0000": - break; - default: - throw new BaseException(HttpStatus.BAD_REQUEST, - false, - resultCode, - resultMessage); - } - } + public String createFlameName(User user) { + List donationUsers = donationAdaptor.findDonationListsByUser(user); + + List inherenceNames = getInherenceName(donationUsers); - public String createFlameName(String name) { String randomName; do { - randomName = name + "님의 " + getRandomEnumValue(AdjectiveFlame.class).getValue() + " " + getRandomEnumValue(Adjective.class).getValue() + " 불꽃이"; - } while (donationUserRepository.existsByInherenceName(randomName)); + randomName = user.getName() + "님의 " + getRandomEnumValue(AdjectiveFlame.class).getValue() + " " + getRandomEnumValue(Adjective.class).getValue() + " 불꽃이"; + } while (inherenceNames.contains(randomName)); return randomName; } @@ -65,19 +59,6 @@ public static > T getRandomEnumValue(Class enumClass) { return values[random.nextInt(values.length)]; } - public void checkBillResult(String resultCode, String resultMsg, String tid, String orderId) { - switch(resultCode){ - case "0000": - niceAuthFeignClient.cancelPayment(getNicePaymentAuthorizationHeader(), tid, new NicePayCancelRequest("결재 확인 완료 취소", orderId)); - break; - default: - throw new BaseException(HttpStatus.BAD_REQUEST, - false, - resultCode, - resultMsg); - } - } - public String maskMiddleNum(String cardNo) { String firstFourDigits = cardNo.substring(0, 4); String lastFourDigits = cardNo.substring(12); @@ -85,4 +66,37 @@ public String maskMiddleNum(String cardNo) { return firstFourDigits + middleDigitsMasked + lastFourDigits; } + + public String createOrderId(String type){ + boolean useLetters = true; + boolean useNumbers = true; + String randomStr = RandomStringUtils.random(12, useLetters, useNumbers); + return type + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + randomStr; + } + + public OrderRes.CreateInherenceDto createInherence(User user) { + + String flameName = createFlameName(user); + + String inherenceNumber = createRandomUUID(); + + return new OrderRes.CreateInherenceDto(flameName, inherenceNumber); + } + + public String createRandomUUID() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + UUID.randomUUID(); + } + + public String formatString(String input, int length) { + StringBuilder formatted = new StringBuilder(); + + for (int i = 0; i < input.length(); i++) { + if (i > 0 && i % length == 0) { + formatted.append('-'); + } + formatted.append(input.charAt(i)); + } + + return formatted.toString(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java index 588c5fe9..bb187b37 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java @@ -1,17 +1,17 @@ package com.example.matchapi.order.service; -import com.example.matchapi.donation.convertor.DonationConvertor; +import com.example.matchapi.donation.service.DonationHistoryService; import com.example.matchapi.order.convertor.OrderConvertor; import com.example.matchapi.order.dto.OrderReq; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.helper.OrderHelper; import com.example.matchapi.portone.service.PaymentService; +import com.example.matchapi.project.service.ProjectService; +import com.example.matchcommon.annotation.RedissonLock; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.exception.BaseException; -import com.example.matchcommon.exception.InternalServerException; -import com.example.matchcommon.exception.NotFoundException; -import com.example.matchcommon.properties.NicePayProperties; import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.adaptor.RegularPaymentAdaptor; import com.example.matchdomain.donation.entity.*; import com.example.matchdomain.donation.entity.enums.CardAbleStatus; import com.example.matchdomain.donation.entity.enums.DonationStatus; @@ -19,208 +19,132 @@ import com.example.matchdomain.donation.entity.enums.RegularStatus; import com.example.matchdomain.donation.repository.*; import com.example.matchdomain.project.entity.Project; -import com.example.matchdomain.project.repository.ProjectRepository; -import com.example.matchdomain.redis.entity.OrderRequest; import com.example.matchdomain.redis.repository.OrderRequestRepository; +import com.example.matchdomain.user.adaptor.UserCardAdaptor; import com.example.matchdomain.user.entity.User; -import com.example.matchdomain.user.repository.UserRepository; -import com.example.matchinfrastructure.pay.nice.client.NiceAuthFeignClient; -import com.example.matchinfrastructure.pay.nice.dto.*; import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.dto.req.PortOnePrepareReq; import com.example.matchinfrastructure.pay.portone.service.PortOneAuthService; import lombok.RequiredArgsConstructor; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.RandomStringUtils; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; import javax.transaction.Transactional; -import javax.validation.Valid; -import java.security.MessageDigest; -import java.text.SimpleDateFormat; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.*; import static com.example.matchcommon.constants.MatchStatic.*; -import static com.example.matchdomain.donation.entity.enums.HistoryStatus.CREATE; -import static com.example.matchdomain.donation.entity.enums.HistoryStatus.TURN_ON; import static com.example.matchdomain.donation.exception.DeleteCardErrorCode.*; import static com.example.matchdomain.donation.exception.DonationGerErrorCode.DONATION_NOT_EXIST; -import static com.example.matchdomain.order.exception.RegistrationCardErrorCode.FAILED_ERROR_ENCRYPT; -import static com.example.matchdomain.project.exception.ProjectOneTimeErrorCode.PROJECT_NOT_EXIST; +import static org.springframework.http.HttpStatus.BAD_REQUEST; @RequiredArgsConstructor @Service public class OrderService { - private final NiceAuthFeignClient niceAuthFeignClient; - private final NicePayProperties nicePayProperties; private final DonationUserRepository donationUserRepository; private final OrderConvertor orderConvertor; private final OrderHelper orderHelper; private final RegularPaymentRepository regularPaymentRepository; private final UserCardRepository userCardRepository; - private final RequestPaymentHistoryRepository requestPaymentHistoryRepository; private final OrderRequestRepository orderRequestRepository; - private final UserRepository userRepository; - private final DonationHistoryRepository donationHistoryRepository; - private final DonationConvertor donationConvertor; private final PortOneFeignClient portOneFeignClient; private final PortOneAuthService portOneAuthService; private final PortOneConvertor portOneConvertor; - private final ProjectRepository projectRepository; private final PaymentService paymentService; + private final ProjectService projectService; + private final RegularPaymentAdaptor regularPaymentAdaptor; + private final UserCardAdaptor userCardAdaptor; + private final DonationHistoryService donationHistoryService; @Transactional - public String requestPayment(User user, OrderReq.OrderDetail orderDetail, Long projectId) { - NicePaymentAuth nicePaymentAuth = niceAuthFeignClient. - paymentAuth(orderHelper.getNicePaymentAuthorizationHeader(), - orderDetail.getTid(), - new NicePayRequest(String.valueOf(orderDetail.getAmount()))); + public List getUserBillCard(Long userId) { + List userCards = userCardAdaptor.findCardLists(userId); - orderHelper.checkNicePaymentsResult(nicePaymentAuth.getResultCode(), nicePaymentAuth.getResultMsg()); + return orderConvertor.convertToUserCardLists(userCards); + } - String flameName = orderHelper.createFlameName(user.getName()); + @RedissonLock(LockName = "카드-삭제", key = "#cardId") + public void deleteBillCard(Long cardId) { + UserCard userCard = userCardAdaptor.findCardByCardId(cardId); - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); + List regularPayments = regularPaymentAdaptor.findByCardId(cardId); - donationUserRepository.save(orderConvertor.donationUser(nicePaymentAuth, user.getId(), orderDetail, projectId, flameName, inherenceNumber)); + cancelRegularPayment(regularPayments); - return flameName; - } + String accessToken = portOneAuthService.getToken(); - @Transactional - public String createRandomUUID() { - return UUID.randomUUID().toString(); - } + portOneFeignClient.deleteBillKey(accessToken, userCard.getBid()); - public String createRandomId(){ - boolean useLetters = true; - boolean useNumbers = true; - String randomStr = RandomStringUtils.random(12, useLetters, useNumbers); + userCard.setStatus(Status.INACTIVE); - return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + randomStr; - } - @Transactional - public List getUserBillCard(Long userId) { - List userCards = userCardRepository.findByUserIdAndStatus(userId,Status.ACTIVE); - List userBillCards = new ArrayList<>(); - - userCards.forEach( - result -> { - userBillCards.add( - new OrderRes.UserBillCard( - result.getId(), - result.getCardCode().getName(), - result.getCardName(), - orderHelper.maskMiddleNum(result.getCardNo()), - result.getCardAbleStatus().getName() - ) - ); - } - ); - return userBillCards; + userCardRepository.save(userCard); } - @Transactional - public void deleteBillCard(Long cardId) { - Optional userCard = userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE); - NiceBillExpireResponse niceBillExpireResponse = niceAuthFeignClient.billKeyExpire(orderHelper.getNicePaymentAuthorizationHeader(), userCard.get().getBid(), new NiceBillExpireRequest(DELETE + createRandomId())); - System.out.println(niceBillExpireResponse.getResultCode() + niceBillExpireResponse.getResultMsg()); - userCard.get().setStatus(Status.INACTIVE); - } + @RedissonLock(LockName = "정기-기부-신청", key = "#cardId") + public OrderRes.CompleteDonation regularDonation(User user, OrderReq.RegularDonation regularDonation, Long cardId, Long projectId) { + UserCard card = userCardAdaptor.findCardByCardId(cardId); + validateCard(card, user); - @Transactional - public void regularDonation(User user, OrderReq.RegularDonation regularDonation, Long cardId, Long projectId) { + Project project = projectService.checkProjectExists(projectId, RegularStatus.REGULAR); - UserCard card = userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); - if(!card.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); - if(!card.getCardAbleStatus().equals(CardAbleStatus.ABLE)) throw new BadRequestException(CARD_NOT_ABLE); - Project project = projectRepository.findByIdAndStatusAndRegularStatus(projectId, Status.ACTIVE, RegularStatus.REGULAR).orElseThrow(() ->new BadRequestException(PROJECT_NOT_EXIST)); + PortOneResponse portOneResponse = paymentService.payBillKey(card, regularDonation.getAmount(), project.getProjectName(), REGULAR); - String orderId = REGULAR + createRandomId(); + OrderRes.CreateInherenceDto createInherenceDto = orderHelper.createInherence(user); - String accessToken = portOneAuthService.getToken(); + RegularPayment regularPayment = regularPaymentRepository.save(orderConvertor.convertToRegularPayment(user.getId(), regularDonation, cardId, projectId)); - PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(card.getBid(), orderId, regularDonation.getAmount(), project.getProjectName(), card.getCustomerId())); + DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), regularDonation.getAmount(), projectId, createInherenceDto, RegularStatus.REGULAR, regularPayment.getId())); - System.out.println(portOneResponse.getCode()); - System.out.println(portOneResponse.getMessage()); + donationHistoryService.postRegularDonationHistory(regularPayment.getId(), donationUser.getId()); - String flameName = orderHelper.createFlameName(user.getName()); + return orderConvertor.convertToCompleteDonation(user.getName(), project, regularDonation.getAmount()); + } - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); - RegularPayment regularPayment = regularPaymentRepository.save(orderConvertor.RegularPayment(user.getId(), regularDonation, cardId, projectId)); - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), regularDonation.getAmount(), projectId, flameName, inherenceNumber, RegularStatus.REGULAR, regularPayment.getId())); - donationHistoryRepository.save(donationConvertor.DonationHistoryTurnOn(regularPayment.getId(), TURN_ON)); - donationHistoryRepository.save(donationConvertor.DonationHistory(donationUser.getId(), CREATE)); - } + private void validateCard(UserCard card, User user) { + if(!card.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); - @Transactional - public void oneTimeDonationCard(User user, OrderReq. @Valid OneTimeDonation oneTimeDonation, Long cardId, Long projectId) { - UserCard card = userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); + if(!card.getCardAbleStatus().equals(CardAbleStatus.ABLE)) throw new BadRequestException(CARD_NOT_ABLE); + } - Project project = projectRepository.findByIdAndStatusAndRegularStatus(projectId, Status.ACTIVE, RegularStatus.ONE_TIME).orElseThrow(() ->new BadRequestException(PROJECT_NOT_EXIST)); + @RedissonLock(LockName = "빌키-단기-기부", key = "#cardId") + public OrderRes.CompleteDonation oneTimeDonationCard(User user, OrderReq.OneTimeDonation oneTimeDonation, Long cardId, Long projectId) { + try { + UserCard card = userCardAdaptor.findCardByCardId(cardId); - String orderId = ONE_TIME + createRandomId(); + validateCard(card, user); - String accessToken = portOneAuthService.getToken(); + Project project = projectService.checkProjectExists(projectId, RegularStatus.ONE_TIME); - PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(card.getBid(), orderId, oneTimeDonation.getAmount(), project.getProjectName(), card.getCustomerId())); + PortOneResponse portOneResponse = paymentService.payBillKey(card, oneTimeDonation.getAmount(), project.getProjectName(), ONE_TIME); - String flameName = orderHelper.createFlameName(user.getName()); + OrderRes.CreateInherenceDto createInherenceDto = orderHelper.createInherence(user); - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); + DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), oneTimeDonation.getAmount(), projectId, + createInherenceDto, RegularStatus.ONE_TIME, null)); - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), oneTimeDonation.getAmount(), projectId, flameName, inherenceNumber, RegularStatus.ONE_TIME, null)); + donationHistoryService.oneTimeDonationHistory(donationUser.getId()); - donationHistoryRepository.save(donationConvertor.DonationHistory(donationUser.getId(), CREATE)); + return orderConvertor.convertToCompleteDonation(user.getName(), project, oneTimeDonation.getAmount()); + }catch (Exception e){ + //paymentService.refundPayment(); + } + return null; } @Transactional public String saveRequest(User user, Long projectId) { - String orderId = ONE_TIME + createRandomId(); + String orderId = orderHelper.createOrderId(ONE_TIME); orderRequestRepository.save(orderConvertor.CreateRequest(user.getId(), projectId, orderId)); return orderId; } - @Transactional - public void requestPaymentAuth(String tid, Long amount) { - NicePaymentAuth nicePaymentAuth = niceAuthFeignClient. - paymentAuth(orderHelper.getNicePaymentAuthorizationHeader(), - tid, - new NicePayRequest(String.valueOf(amount))); - - orderHelper.checkNicePaymentsResult(nicePaymentAuth.getResultCode(), nicePaymentAuth.getResultMsg()); - - Optional orderRequest = orderRequestRepository.findById(nicePaymentAuth.getOrderId()); - - Optional user = userRepository.findByIdAndStatus(Long.valueOf(orderRequest.get().getUserId()),Status.ACTIVE); - - String flameName = orderHelper.createFlameName(user.get().getName()); - - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); - - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationUserV2(nicePaymentAuth, user.get().getId(), amount, orderRequest.get().getProjectId(), flameName, inherenceNumber)); - - donationHistoryRepository.save(donationConvertor.DonationHistory(donationUser.getId(), CREATE)); - - } - - @Transactional + @RedissonLock(LockName = "관리자-환불-처리", key = "#donationUserId") public void adminRefundDonation(Long donationUserId) { DonationUser donationUser = donationUserRepository.findById(donationUserId).orElseThrow(()-> new BadRequestException(DONATION_NOT_EXIST)); donationUser.setDonationStatus(DonationStatus.EXECUTION_REFUND); @@ -237,7 +161,7 @@ public void modifyDonationStatus(Long donationUserId, DonationStatus donationSta @Transactional public void revokePay(User user, Long cardId) { - UserCard userCard = userCardRepository.findByIdAndStatus(cardId, Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); + UserCard userCard = userCardAdaptor.findCardByCardId(cardId); if(!userCard.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); List regularPayments = regularPaymentRepository.findByUserCardId(cardId); @@ -248,39 +172,43 @@ public void revokePay(User user, Long cardId) { regularPaymentRepository.saveAll(regularPayments); } - public final synchronized String getyyyyMMddHHmmss(){ - SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss"); - return yyyyMMddHHmmss.format(new Date()); - } - + @RedissonLock(LockName = "유저-카드-등록", key = "#user") public PortOneBillResponse postCard(User user, OrderReq.RegistrationCard registrationCard) { String accessToken = portOneAuthService.getToken(); - String customerUid = BILL + createRandomId(); - String cardNo = formatString(registrationCard.getCardNo(), 4); + String cardNo = orderHelper.formatString(registrationCard.getCardNo(), 4); String expiry = "20" + registrationCard.getExpYear() + "-" + registrationCard.getExpMonth(); PortOneResponse portOneResponse = portOneFeignClient.getBillKey( accessToken, - customerUid, - portOneConvertor.PortOneBill(cardNo, expiry, registrationCard.getIdNo(), registrationCard.getCardPw()) + orderHelper.createOrderId(BILL), + portOneConvertor.convertToPortOneBill(cardNo, expiry, registrationCard.getIdNo(), registrationCard.getCardPw()) ); + if(portOneResponse.getCode()!=0){ - throw new BaseException(HttpStatus.BAD_REQUEST, false, "PORT_ONE_BILL_AUTH_001", portOneResponse.getMessage()); + throw new BaseException(BAD_REQUEST, false, "PORT_ONE_BILL_AUTH_001", portOneResponse.getMessage()); } - UserCard userCard = userCardRepository.save(orderConvertor.UserBillCard(user.getId(), registrationCard, portOneResponse.getResponse())); + userCardRepository.save(orderConvertor.convertToUserBillCard(user.getId(), registrationCard, portOneResponse.getResponse())); return portOneResponse.getResponse(); } - public static String formatString(String input, int length) { - StringBuilder formatted = new StringBuilder(); - for (int i = 0; i < input.length(); i++) { - if (i > 0 && i % length == 0) { - formatted.append('-'); - } - formatted.append(input.charAt(i)); + + private void cancelRegularPayment(List regularPayments) { + for(RegularPayment regularPayment : regularPayments){ + regularPayment.setRegularPayStatus(RegularPayStatus.USER_CANCEL); } + regularPaymentRepository.saveAll(regularPayments); + } + + public String saveRequestPrepare(User user, Long projectId, int amount) { + String orderId = orderHelper.createOrderId(ONE_TIME); + + orderRequestRepository.save(orderConvertor.convertToRequestPrepare(user.getId(), projectId, amount, orderId)); - return formatted.toString(); + PortOnePrepareReq portOnePrepareReq = portOneConvertor.convertToRequestPrepare(orderId, amount); + + portOneFeignClient.preparePayments(portOneAuthService.getToken(), portOnePrepareReq); + + return orderId; } } \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java b/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java index 414bb564..af4e5a53 100644 --- a/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java +++ b/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java @@ -1,5 +1,6 @@ package com.example.matchapi.portone.controller; +import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.portone.dto.PaymentReq; import com.example.matchapi.portone.service.PaymentService; import com.example.matchapi.project.dto.ProjectReq; @@ -17,9 +18,8 @@ public class PaymentController { private final PaymentService paymentService; @PostMapping("/validate") @Operation(summary = "08-01 Payment 가격 검증💸") - public CommonResponse validatePayment(@RequestBody PaymentReq.ValidatePayment validatePayment){ - paymentService.checkPayment(validatePayment); - return CommonResponse.onSuccess("결제 성공"); + public CommonResponse validatePayment(@RequestBody PaymentReq.ValidatePayment validatePayment){ + return CommonResponse.onSuccess(paymentService.checkPayment(validatePayment)); } @PostMapping("/refund") diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java b/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java index 905651e6..22ed564f 100644 --- a/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java +++ b/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java @@ -1,44 +1,65 @@ package com.example.matchapi.portone.service; +import com.example.matchapi.donation.service.DonationHistoryService; import com.example.matchapi.order.convertor.OrderConvertor; +import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.helper.OrderHelper; import com.example.matchapi.portone.dto.PaymentReq; +import com.example.matchcommon.annotation.RedissonLock; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.properties.PortOneProperties; import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.UserCard; import com.example.matchdomain.donation.repository.DonationUserRepository; +import com.example.matchdomain.project.adaptor.ProjectAdaptor; +import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.redis.entity.OrderRequest; import com.example.matchdomain.redis.repository.OrderRequestRepository; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.repository.UserRepository; +import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; +import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.service.PortOneAuthService; +import com.example.matchinfrastructure.pay.portone.service.PortOneService; import com.siot.IamportRestClient.IamportClient; import com.siot.IamportRestClient.exception.IamportResponseException; import com.siot.IamportRestClient.request.CancelData; import com.siot.IamportRestClient.response.IamportResponse; import com.siot.IamportRestClient.response.Payment; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Optional; import java.util.UUID; +import static com.example.matchcommon.constants.MatchStatic.ONE_TIME; import static com.example.matchdomain.order.exception.PortOneAuthErrorCode.*; +import static com.example.matchdomain.project.exception.ProjectOneTimeErrorCode.PROJECT_NOT_EXIST; +import static com.example.matchdomain.user.exception.UserLoginErrorCode.NOT_EXIST_USER; @Service +@Slf4j @RequiredArgsConstructor public class PaymentService { - private final PortOneProperties portOneProperties; private final OrderRequestRepository orderRequestRepository; private final UserRepository userRepository; private final OrderHelper orderHelper; private final DonationUserRepository donationUserRepository; private final OrderConvertor orderConvertor; private final IamportClient iamportClient; + private final ProjectAdaptor projectAdaptor; + private final DonationHistoryService donationHistoryService; + private final PortOneProperties portOneProperties; + private final PortOneConvertor portOneConvertor; + private final PortOneFeignClient portOneFeignClient; + private final PortOneAuthService portOneAuthService; + @Autowired public PaymentService(PortOneProperties portOneProperties, @@ -46,7 +67,7 @@ public PaymentService(PortOneProperties portOneProperties, UserRepository userRepository, OrderHelper orderHelper, DonationUserRepository donationUserRepository, - OrderConvertor orderConvertor) { + OrderConvertor orderConvertor, ProjectAdaptor projectAdaptor, DonationHistoryService donationHistoryService, PortOneConvertor portOneConvertor, PortOneFeignClient portOneFeignClient, PortOneAuthService portOneAuthService) { this.portOneProperties = portOneProperties; this.orderRequestRepository = orderRequestRepository; this.userRepository = userRepository; @@ -54,34 +75,43 @@ public PaymentService(PortOneProperties portOneProperties, this.donationUserRepository = donationUserRepository; this.orderConvertor = orderConvertor; this.iamportClient = new IamportClient(portOneProperties.getKey(), portOneProperties.getSecret()); + this.projectAdaptor = projectAdaptor; + this.donationHistoryService = donationHistoryService; + this.portOneConvertor = portOneConvertor; + this.portOneFeignClient = portOneFeignClient; + this.portOneAuthService = portOneAuthService; } - public void checkPayment(PaymentReq.ValidatePayment validatePayment){ + @RedissonLock(LockName = "결제-검증", key = "#validatePayment.orderId") + public OrderRes.CompleteDonation checkPayment(PaymentReq.ValidatePayment validatePayment){ try { OrderRequest orderRequest = orderRequestRepository.findById(validatePayment.getOrderId()).orElseThrow(()->new BadRequestException(NOT_EXIST_ORDER_ID)); + + log.info(orderRequest.getOrderId()); + IamportResponse payment = iamportClient.paymentByImpUid(validatePayment.getImpUid()); - Optional user = userRepository.findByIdAndStatus(Long.valueOf(orderRequest.getUserId()), Status.ACTIVE); - int paidAmount = payment.getResponse().getAmount().intValue(); //사용자가 결제한 금액 - - if(paidAmount!=validatePayment.getAmount()){ - CancelData cancelData = createCancelData(payment, 0); - iamportClient.cancelPaymentByImpUid(cancelData); - throw new BadRequestException(FAILED_ERROR_AUTH_AMOUNT); - }else{ - String flameName = orderHelper.createFlameName(user.get().getName()); - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); - donationUserRepository.save(orderConvertor.donationUserPortone(payment.getResponse(), user.get().getId(), validatePayment, Long.valueOf(orderRequest.getProjectId()), flameName, inherenceNumber)); + + if(payment.getResponse().getAmount().intValue()!=validatePayment.getAmount()) throw new BadRequestException(FAILED_ERROR_AUTH_AMOUNT); + + User user = userRepository.findByIdAndStatus(Long.valueOf(orderRequest.getUserId()), Status.ACTIVE).orElseThrow(()->new BadRequestException(NOT_EXIST_USER)); + + Project project = projectAdaptor.findByProjectId(Long.valueOf(orderRequest.getProjectId())).orElseThrow(()->new BadRequestException(PROJECT_NOT_EXIST)); + + saveDonationUser(user, validatePayment, project); + + orderRequestRepository.deleteById(validatePayment.getOrderId()); + + return orderConvertor.convertToCompleteDonation(user.getName(), project, (long) validatePayment.getAmount()); + } catch (BadRequestException | IamportResponseException | IOException e) { + try { + refundPayment(validatePayment.getImpUid()); + } catch(Exception ex) { + System.out.println(ex.getMessage()); } - } catch (IamportResponseException | IOException e) { - System.out.println(e.getMessage()); throw new BadRequestException(FAILED_ERROR_AUTH); } } - public String createRandomUUID() { - return UUID.randomUUID().toString(); - } - private CancelData createCancelData(IamportResponse response, int refundAmount) { if (refundAmount == 0) { //전액 환불일 경우 return new CancelData(response.getResponse().getImpUid(), true); @@ -96,4 +126,16 @@ public void refundPayment(String impUid) { throw new RuntimeException(e); } } + + public void saveDonationUser(User user, PaymentReq.ValidatePayment validatePayment, Project project) { + OrderRes.CreateInherenceDto createInherenceDto = orderHelper.createInherence(user); + DonationUser donationUser = donationUserRepository.save(orderConvertor.convertToDonationUserPortone(user.getId(), validatePayment, project.getId(), createInherenceDto)); + donationHistoryService.oneTimeDonationHistory(donationUser.getId()); + } + + public PortOneResponse payBillKey(UserCard card, Long amount, String projectName, String type) { + String orderId = orderHelper.createOrderId(type); + String accessToken = portOneAuthService.getToken(); + return portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.convertPayWithBillKey(card.getBid(), orderId, amount, projectName, card.getCustomerId())); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java b/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java index 0a6a7717..d192e573 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/controller/ProjectController.java @@ -4,6 +4,7 @@ import com.example.matchapi.donation.service.DonationService; import com.example.matchapi.project.dto.ProjectReq; import com.example.matchapi.project.dto.ProjectRes; +import com.example.matchcommon.constants.enums.FILTER; import com.example.matchapi.project.service.ProjectService; import com.example.matchcommon.annotation.ApiErrorCodeExample; import com.example.matchcommon.exception.errorcode.RequestErrorCode; @@ -37,7 +38,7 @@ public class ProjectController { private final ProjectService projectService; private final DonationService donationService; - @Operation(summary = "03-01💻 프로젝트 리스트 조회 API.",description = "프로젝트 리스트 조회 API 입니다.") + @Operation(summary = "03-01💻 프로젝트 리스트 조회 API. #Web version",description = "프로젝트 리스트 조회 API 입니다.") @GetMapping("") public CommonResponse>> getProjectList( @Parameter(hidden = true) @AuthenticationPrincipal User user, @@ -47,7 +48,7 @@ public CommonResponse>> getProjectList return CommonResponse.onSuccess(projectService.getProjectList(user, page, size)); } - @Operation(summary = "03-02💻 프로젝트 상세조회 API.",description = "프로젝트 상세조회 API 입니다.") + @Operation(summary = "03-02💻 프로젝트 상세조회 API. #Web version",description = "프로젝트 상세조회 API 입니다.") @GetMapping("/{projectId}") @CheckIdExist @ApiErrorCodeExample({ProjectOneTimeErrorCode.class}) @@ -59,7 +60,7 @@ public CommonResponse getProject( } - @Operation(summary = "03-03💻 프로젝트 검색 조회",description = "프로젝트 검색 조회 API 입니다.") + @Operation(summary = "03-03💻 프로젝트 검색 조회 #Web version",description = "프로젝트 검색 조회 API 입니다.") @GetMapping("/search") public CommonResponse>> searchProjectList( @Parameter(hidden = true) @AuthenticationPrincipal User user, @@ -71,18 +72,6 @@ public CommonResponse>> searchProjectL return CommonResponse.onSuccess(projectService.searchProjectList(user, content, page, size)); } - @Operation(summary = "03-04💻 프로젝트 댓글 조회",description = "프로젝트 댓글 조회 API 입니다.") - @GetMapping("/comment/{projectId}") - public CommonResponse>> getProjectComment( - @Parameter(hidden = true) @AuthenticationPrincipal User user, - @Parameter(description = "페이지", example = "0") @RequestParam(required = true, defaultValue = "0") @Min(value = 0) int page, - @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = true, defaultValue = "10") int size, - @Parameter(description = "프로젝트 id") @PathVariable("projectId") Long projectId - ){ - log.info("03-04 프로젝트 댓글 조회 projectId : "+ projectId); - return CommonResponse.onSuccess(projectService.getProjectComment(user, projectId, page, size)); - } - @Operation(summary = "03-05💻 프로젝트 리스트 조회 API #FRAME_프로젝트 리스트 조회.",description = "프로젝트 리스트 조회 API 입니다.") @GetMapping("/list") @ApiErrorCodeExample(UserAuthErrorCode.class) @@ -91,10 +80,11 @@ public CommonResponse>> getProjectLis @Parameter(description = "페이지", example = "0") @RequestParam(required = true, defaultValue = "0") @Min(value = 0) int page, @Parameter(description = "페이지 사이즈", example = "10") @RequestParam(required = true, defaultValue = "10") int size, @Parameter(description = "후원종류") @RequestParam(required = false)ProjectKind projectKind, - @Parameter(description = "검색어") @RequestParam(required = false) String content - ) { + @Parameter(description = "검색어") @RequestParam(required = false) String content, + @Parameter(description = "필터종류 \n최신순 = LATEST \n추천순 RECOMMEND ") @RequestParam(required = false, defaultValue = "LATEST") FILTER filter + ) { log.info("03-05 프로젝트 리스트 조회"); - return CommonResponse.onSuccess(projectService.getProjectLists(user, page, size, projectKind, content)); + return CommonResponse.onSuccess(projectService.getProjectLists(user, page, size, projectKind, content, filter)); } @Operation(summary = "03-06💻 프로젝트 관심설정/관심삭제 API #FRAME_프로젝트 리스트 조회.",description = "프로젝트 관심 설정/삭제 API 입니다.") diff --git a/Match-Api/src/main/java/com/example/matchapi/project/convertor/ProjectConvertor.java b/Match-Api/src/main/java/com/example/matchapi/project/convertor/ProjectConvertor.java index 9c0e9e52..82bf3234 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/convertor/ProjectConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/convertor/ProjectConvertor.java @@ -1,5 +1,6 @@ package com.example.matchapi.project.convertor; +import com.example.matchapi.common.util.TimeHelper; import com.example.matchapi.donation.helper.DonationHelper; import com.example.matchapi.project.dto.ProjectReq; import com.example.matchapi.project.dto.ProjectRes; @@ -31,7 +32,6 @@ public class ProjectConvertor { private final ProjectHelper projectHelper; private final RegularPaymentRepository regularPaymentRepository; - private final DonationHelper donationHelper; private static final String FIRST_TIME = "T00:00:00"; private static final String LAST_TIME = "T23:59:59"; public ProjectRes.ProjectDetail projectImgList(List projectImage) { @@ -74,25 +74,13 @@ public UserRes.MyPage getMyPage(List regularPayments, Long likeC } return UserRes.MyPage.builder() - .username(name) + .name(name) .likeCnt(Math.toIntExact(likeCnt)) .underCnt(underCnt) .successCnt(successCnt) .build(); } - - public ProjectRes.CommentList projectComment(Long userId, ProjectComment result) { - return ProjectRes.CommentList.builder() - .commentId(result.getId()) - .comment(result.getComment()) - .commentDate(donationHelper.dayTimeFormat(result.getCreatedAt())) - .nickname(result.getUser().getNickname()) - .userId(result.getUserId()) - .isMy(result.getUserId().equals(userId)) - .build(); - } - public Project postProject(ProjectReq.Project projects) { return Project.builder() .projectName(projects.getProjectName()) @@ -117,7 +105,7 @@ public ProjectImage postProjectImage(Long id, String imgUrl, ImageRepresentStatu .build(); } - public ProjectRes.ProjectAdminList ProjectList(ProjectRepository.ProjectAdminList result) { + public ProjectRes.ProjectAdminList convertToProjectList(ProjectRepository.ProjectAdminList result) { return ProjectRes.ProjectAdminList.builder() .projectId(result.getProjectId()) .projectName(result.getProjectName()) @@ -130,7 +118,7 @@ public ProjectRes.ProjectAdminList ProjectList(ProjectRepository.ProjectAdminLis .build(); } - public ProjectRes.ProjectAdminDetail ProjectAdminDetail(ProjectRepository.ProjectAdminDetail result, List projectImages) { + public ProjectRes.ProjectAdminDetail convertToProjectAdminDetail(ProjectRepository.ProjectAdminDetail result, List projectImages) { List projectImgLists = new ArrayList<>(); projectImages.forEach( results -> projectImgLists.add( @@ -162,7 +150,18 @@ public ProjectRes.ProjectAdminDetail ProjectAdminDetail(ProjectRepository.Projec .build(); } - public ProjectRes.DonationList DonationUserInfo(DonationUser result) { + public List convertToDonationUserInfo(List donationUsers){ + List donationLists = new ArrayList<>(); + donationUsers.forEach( + result -> donationLists.add( + convertToDonationUserInfoDetail(result) + ) + ); + + return donationLists; + } + + public ProjectRes.DonationList convertToDonationUserInfoDetail(DonationUser result) { return ProjectRes.DonationList .builder() .donationId(result.getId()) @@ -181,7 +180,7 @@ public ProjectRes.DonationList DonationUserInfo(DonationUser result) { .build(); } - public ProjectRes.ProjectLists ProjectLists(ProjectRepository.ProjectList result) { + public ProjectRes.ProjectLists convertToProjectListsDetail(ProjectRepository.ProjectList result) { List imgUrlList = new ArrayList<>(); if(result.getImgUrlList()!=null){ imgUrlList = Stream.of(result.getImgUrlList().split(",")).collect(Collectors.toList()); @@ -202,26 +201,23 @@ public ProjectRes.ProjectLists ProjectLists(ProjectRepository.ProjectList result .build(); } - public ProjectRes.ProjectLists ProjectListQueryDsl(ProjectList result) { - return ProjectRes.ProjectLists - .builder() - .projectId(result.getId()) - .imgUrl(result.getImgUrl()) - .title(result.getProjectName()) - .usages(result.getUsages()) - .kind(result.getProjectKind().getName()) - .like(result.getLike()) - .userProfileImages(result.getImgUrlList()) - .totalDonationCnt(Math.toIntExact(result.getTotalDonationCnt())) - .build(); + public List convertToProjectLists(List projects){ + List projectLists = new ArrayList<>(); + projects.forEach( + result -> { + projectLists.add(convertToProjectListsDetail(result)); + } + ); + + return projectLists; } - public ProjectRes.ProjectAppDetail ProjectAppDetail(ProjectRepository.ProjectDetail projects, List projectImages) { + public ProjectRes.ProjectAppDetail convertToProjectAppDetail(ProjectRepository.ProjectDetail projects, List projectImages) { List projectImgLists = new ArrayList<>(); String thumbNail = ""; for(ProjectImage projectImage : projectImages){ if(projectImage.getImageRepresentStatus() == ImageRepresentStatus.NORMAL){ - projectImgLists.add(ProjectImages(projectImage)); + projectImgLists.add(convertToProjectImages(projectImage)); } else { thumbNail = projectImage.getUrl(); @@ -250,7 +246,7 @@ public ProjectRes.ProjectAppDetail ProjectAppDetail(ProjectRepository.ProjectDet .build(); } - private ProjectRes.ProjectImgList ProjectImages(ProjectImage projectImage) { + private ProjectRes.ProjectImgList convertToProjectImages(ProjectImage projectImage) { return ProjectRes.ProjectImgList .builder() .imgId(projectImage.getId()) @@ -259,7 +255,7 @@ private ProjectRes.ProjectImgList ProjectImages(ProjectImage projectImage) { .build(); } - public ProjectRes.ProjectLists ProjectToDto(ProjectDto result) { + public ProjectRes.ProjectLists convertToProjectToDto(ProjectDto result) { List imgUrlList = new ArrayList<>(); List regularPayments = regularPaymentRepository.findByProjectIdAndRegularPayStatus(result.getId(), RegularPayStatus.PROCEEDING); @@ -280,7 +276,7 @@ public ProjectRes.ProjectLists ProjectToDto(ProjectDto result) { .build(); } - public ProjectComment Comment(Long id, Long projectId, String comment) { + public ProjectComment convertToComment(Long id, Long projectId, String comment) { return ProjectComment .builder() .userId(id) @@ -289,7 +285,7 @@ public ProjectComment Comment(Long id, Long projectId, String comment) { .build(); } - public DonationHistory DonationHistory(Long projectId, HistoryStatus historyStatus) { + public DonationHistory convertToDonationHistory(Long projectId, HistoryStatus historyStatus) { return DonationHistory .builder() .projectId(projectId) @@ -297,11 +293,49 @@ public DonationHistory DonationHistory(Long projectId, HistoryStatus historyStat .build(); } - public CommentReport ReportComment(Long commentId, ReportReason reportReason) { + public CommentReport convertToReportComment(Long commentId, ReportReason reportReason) { return CommentReport .builder() .commentId(commentId) .reportReason(reportReason) .build(); } + + public List convertToProjectListWeb(List projects) { + List projectLists = new ArrayList<>(); + + projects.forEach( + result -> { + projectLists.add(new ProjectRes.ProjectList( + result.getId(), + result.getImgUrl(), + result.getProjectName(), + result.getUsages(), + result.getProjectKind(), + result.getLike() + )); + } + ); + + return projectLists; + } + + public List convertToProjectListWebForNotLogin(List projects) { + List projectLists = new ArrayList<>(); + + projects.forEach( + result -> { + String imageUrl = result.getProjectImage().isEmpty() ? null : result.getProjectImage().get(0).getUrl(); + projectLists.add(new ProjectRes.ProjectList( + result.getId(), + imageUrl, + result.getProjectName(), + result.getUsages(), + result.getProjectKind().getValue(), + false + )); + } + ); + return projectLists; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java b/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java index 56ed0877..bf5478ba 100644 --- a/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java +++ b/Match-Api/src/main/java/com/example/matchapi/project/service/ProjectService.java @@ -3,18 +3,22 @@ import com.example.matchapi.project.convertor.ProjectConvertor; import com.example.matchapi.project.dto.ProjectReq; import com.example.matchapi.project.dto.ProjectRes; +import com.example.matchcommon.constants.enums.FILTER; import com.example.matchapi.user.helper.AuthHelper; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.exception.NotFoundException; import com.example.matchcommon.reponse.PageResponse; import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.adaptor.DonationAdaptor; import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import com.example.matchdomain.donation.entity.enums.RegularStatus; import com.example.matchdomain.donation.repository.DonationHistoryRepository; import com.example.matchdomain.donation.repository.DonationUserRepository; +import com.example.matchdomain.project.adaptor.ProjectAdaptor; +import com.example.matchdomain.project.adaptor.ProjectImgAdaptor; import com.example.matchdomain.project.dto.ProjectDto; import com.example.matchdomain.project.entity.*; -import com.example.matchdomain.project.entity.enums.ImageRepresentStatus; import com.example.matchdomain.project.entity.enums.ProjectKind; import com.example.matchdomain.project.entity.enums.ProjectStatus; import com.example.matchdomain.project.entity.enums.ReportReason; @@ -39,10 +43,8 @@ import static com.example.matchdomain.project.entity.enums.ImageRepresentStatus.NORMAL; import static com.example.matchdomain.project.entity.enums.ImageRepresentStatus.REPRESENT; import static com.example.matchdomain.project.entity.enums.ProjectStatus.PROCEEDING; -import static com.example.matchdomain.project.entity.enums.TodayStatus.TODAY; import static com.example.matchdomain.project.exception.CommentDeleteErrorCode.COMMENT_DELETE_ERROR_CODE; import static com.example.matchdomain.project.exception.CommentGetErrorCode.COMMENT_NOT_EXIST; -import static com.example.matchdomain.project.exception.PatchProjectImageErrorCode.PROJECT_IMAGE_NOT_EXIST; import static com.example.matchdomain.project.exception.PatchProjectImageErrorCode.PROJECT_NOT_CORRECT_IMAGE; import static com.example.matchdomain.project.exception.ProjectGetErrorCode.PROJECT_NOT_EXIST; @@ -50,150 +52,59 @@ @Service @RequiredArgsConstructor public class ProjectService { + private final ProjectAdaptor projectAdaptor; private final ProjectRepository projectRepository; private final ProjectConvertor projectConvertor; private final ProjectImageRepository projectImageRepository; private final AuthHelper authHelper; private final ProjectCommentRepository projectCommentRepository; private final S3UploadService s3UploadService; - private final DonationUserRepository donationUserRepository; private final ProjectUserAttentionRepository projectUserAttentionRepository; private final DonationHistoryRepository donationHistoryRepository; private final CommentReportRepository commentReportRepository; + private final ProjectImgAdaptor projectImgAdaptor; + private final DonationAdaptor donationAdaptor; public PageResponse> getProjectList(User user, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - Long userId = 0L; - if(authHelper.checkGuest(user)) userId = user.getId(); - - List projectLists = new ArrayList<>(); + if(authHelper.checkGuest(user)) userId = user.getId(); if(!userId.equals(0L)){ - Page projects = projectRepository.findLoginUserProjectList(userId, PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); - projects.getContent().forEach( - result -> { - projectLists.add(new ProjectRes.ProjectList( - result.getId(), - result.getImgUrl(), - result.getProjectName(), - result.getUsages(), - result.getProjectKind(), - result.getLike() - )); - } - ); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectLists); - } + Page projects = projectAdaptor.findLoginUserProjectList(userId, page, size); - else{ - Page projects = projectRepository.findByStatusAndProjectStatusAndFinishedAtGreaterThanEqualAndProjectImage_ImageRepresentStatusOrderByViewCnt(ACTIVE, PROCEEDING, LocalDateTime.now(), ImageRepresentStatus.REPRESENT, pageable); - projects.getContent().forEach( - result -> { - String imageUrl = result.getProjectImage().isEmpty() ? null : result.getProjectImage().get(0).getUrl(); - projectLists.add(new ProjectRes.ProjectList( - result.getId(), - imageUrl, - result.getProjectName(), - result.getUsages(), - result.getProjectKind().getValue(), - false - )); - } - ); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectLists); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWeb(projects.getContent())); + } else{ + Page projects = projectAdaptor.findNotLoginUserProjectList(page, size); + + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWebForNotLogin(projects.getContent())); } } public ProjectRes.ProjectDetail getProjectDetail(User user, Long projectId) { - List projectImage = projectImageRepository.findByProjectIdAndImageRepresentStatusAndProject_StatusOrderBySequenceAsc(projectId, ImageRepresentStatus.NORMAL, ACTIVE); - return projectConvertor.projectImgList(projectImage); + return projectConvertor.projectImgList(projectImgAdaptor.getProjectDetail(projectId)); } public PageResponse> searchProjectList(User user, String content, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - - - List projectLists = new ArrayList<>(); - Long userId = 0L; + if(authHelper.checkGuest(user)) userId = user.getId(); if(!userId.equals(0L)){ + Page projects = projectAdaptor.findLoginUserSearchProjectList(userId, page, size, content); - Page projects = projectRepository.searchProjectLoginUser(userId,content,content,content, PROCEEDING.getValue(),LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(),pageable, ACTIVE.getValue()); - - projects.getContent().forEach( - result -> { - projectLists.add(new ProjectRes.ProjectList( - result.getId(), - result.getImgUrl(), - result.getProjectName(), - result.getUsages(), - result.getProjectKind(), - result.getLike() - )); - } - ); - - - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectLists); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWeb(projects.getContent())); + } else{ + Page projects = projectAdaptor.findNotLoginUserSearchProjectList(content, page, size); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectListWebForNotLogin(projects.getContent())); } - else{ - Page projects = projectRepository.searchProject(content,content,content, PROCEEDING,LocalDateTime.now(), ImageRepresentStatus.REPRESENT,pageable, ACTIVE); - - projects.getContent().forEach( - result -> { - String imageUrl = result.getProjectImage().isEmpty() ? null : result.getProjectImage().get(0).getUrl(); - projectLists.add(new ProjectRes.ProjectList( - result.getId(), - imageUrl, - result.getProjectName(), - result.getUsages(), - result.getProjectKind().getValue(), - false - )); - } - ); - - - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectLists); - } - - } - - public PageResponse> getProjectComment(User user, Long projectId, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - - Long userId; - if(authHelper.checkGuest(user)) userId = user.getId(); - else { - userId = 0L; - } - - Page projectComments = projectCommentRepository.findByProjectIdAndStatusOrderByCreatedAtAsc(projectId, ACTIVE, pageable); - - List commentLists = new ArrayList<>(); - projectComments.getContent().forEach( - result-> { - commentLists.add( - projectConvertor.projectComment(userId, result) - ); - } - ); - - - return new PageResponse<>(projectComments.isLast(), projectComments.getTotalElements(), commentLists); } @Transactional public void postProject(ProjectReq.Project projects, MultipartFile presentFile, List multipartFiles) { - - System.out.println(projects.getProjectName()); Project project = projectRepository.save(projectConvertor.postProject(projects)); String url = s3UploadService.uploadProjectPresentFile(project.getId() ,presentFile); @@ -202,7 +113,7 @@ public void postProject(ProjectReq.Project projects, MultipartFile presentFile, saveImgList(project.getId(), url, imgUrlList); - donationHistoryRepository.save(projectConvertor.DonationHistory(project.getId(), HistoryStatus.TURN_ON)); + donationHistoryRepository.save(projectConvertor.convertToDonationHistory(project.getId(), HistoryStatus.TURN_ON)); } public PageResponse> getProjectList(int page, int size) { @@ -214,7 +125,7 @@ public PageResponse> getProjectList(int page, projectAdminLists.getContent().forEach( result -> projectLists.add( - projectConvertor.ProjectList(result) + projectConvertor.convertToProjectList(result) ) ); @@ -223,17 +134,17 @@ public PageResponse> getProjectList(int page, @Transactional public void patchProjectStatus(ProjectStatus projectStatus, Long projectId) { - Project project = projectRepository.findById(projectId).orElseThrow(()-> new NotFoundException(PROJECT_NOT_EXIST)); + Project project = projectAdaptor.findById(projectId); project.setProjectStatus(projectStatus); - if(projectStatus.equals(ProjectStatus.DEADLINE)) donationHistoryRepository.save(projectConvertor.DonationHistory(projectId, HistoryStatus.FINISH)); + if(projectStatus.equals(ProjectStatus.DEADLINE)) donationHistoryRepository.save(projectConvertor.convertToDonationHistory(projectId, HistoryStatus.FINISH)); projectRepository.save(project); } public void deleteProject(Long projectId) { - Project project = projectRepository.findById(projectId).orElseThrow(()-> new NotFoundException(PROJECT_NOT_EXIST)); + Project project = projectAdaptor.findById(projectId); project.setStatus(Status.INACTIVE); @@ -242,7 +153,7 @@ public void deleteProject(Long projectId) { @Transactional public void patchProject(Long projectId, ProjectReq.ModifyProject modifyProject) { - Project project = projectRepository.findById(projectId).orElseThrow(()-> new NotFoundException(PROJECT_NOT_EXIST)); + Project project = projectAdaptor.findById(projectId); project.modifyProject(modifyProject.getProjectName(), modifyProject.getUsages(), modifyProject.getDetail(), modifyProject.getRegularStatus(), modifyProject.getStartDate(), modifyProject.getEndDate(), modifyProject.getProjectKind(), modifyProject.getSearchKeyword()); @@ -267,79 +178,50 @@ public void saveImgList(Long id, String url, List imgUrlList) { @Transactional public ProjectRes.ProjectAdminDetail getProjectAdminDetail(Long projectId) { - ProjectRepository.ProjectAdminDetail projectAdminDetail = projectRepository.getProjectAdminDetail(projectId); - if(projectAdminDetail == null) throw new BadRequestException(PROJECT_NOT_EXIST); - List projectImages = projectImageRepository.findByProjectIdOrderBySequenceAsc(projectId); - return projectConvertor.ProjectAdminDetail(projectAdminDetail,projectImages); - } + ProjectRepository.ProjectAdminDetail projectAdminDetail = projectAdaptor.projectAdminDetail(projectId); - public PageResponse> getDonationList(Long projectId, int page, int size) { - Pageable pageable = PageRequest.of(page, size); + if(projectAdminDetail == null) throw new BadRequestException(PROJECT_NOT_EXIST); - List donationLists = new ArrayList<>(); + List projectImages = projectImgAdaptor.findProjectImages(projectId); - Page donationUsers = donationUserRepository.findByProjectId(projectId, pageable); + return projectConvertor.convertToProjectAdminDetail(projectAdminDetail,projectImages); + } - donationUsers.getContent().forEach( - result -> donationLists.add( - projectConvertor.DonationUserInfo(result) - ) - ); + public PageResponse> getDonationList(Long projectId, int page, int size) { + Page donationUsers = donationAdaptor.findDonationUsers(projectId, page, size); - return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), donationLists); + return new PageResponse<>(donationUsers.isLast(), donationUsers.getTotalElements(), projectConvertor.convertToDonationUserInfo(donationUsers.getContent())); } @Transactional public ProjectRes.PatchProjectImg modifyProjectImg(Long projectId, Long projectImgId, MultipartFile multipartFile) { + ProjectImage projectImage = projectImgAdaptor.findById(projectImgId); - ProjectImage projectImage = projectImageRepository.findById(projectImgId).orElseThrow(()-> new BadRequestException(PROJECT_IMAGE_NOT_EXIST)); if(!projectImage.getProjectId().equals(projectId)) throw new BadRequestException(PROJECT_NOT_CORRECT_IMAGE); + String imgUrl = s3UploadService.uploadProjectPresentFile(projectId, multipartFile); + s3UploadService.deleteFile(projectImage.getUrl()); + projectImage.setUrl(imgUrl); + projectImageRepository.save(projectImage); + return new ProjectRes.PatchProjectImg(projectImgId, projectImage.getUrl()); } public void patchProjectActive(Long projectId) { - Project project = projectRepository.findById(projectId).orElseThrow(()-> new NotFoundException(PROJECT_NOT_EXIST)); + Project project = projectAdaptor.findById(projectId); project.setStatus(ACTIVE); projectRepository.save(project); } - public PageResponse> getProjectLists(User user, int page, int size, ProjectKind projectKind, String content) { - Pageable pageable = PageRequest.of(page, size); - Page projects = null; - List project = new ArrayList<>(); - - if(projectKind == null){ - if(content == null){ - projects = projectRepository.findLoginUserProjectList(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); - } - else{ - projects = projectRepository.findByContent(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), content); - - } - }else{ - if(content == null){ - projects = projectRepository.findByProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), - ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue()); - - } - else{ - projects = projectRepository.findByContentAndProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), - ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue(), content); - } - } + public PageResponse> getProjectLists(User user, int page, int size, ProjectKind projectKind, String content, FILTER filter) { + Page projects = projectAdaptor.findProject(user, page, size, projectKind, content, filter); - projects.getContent().forEach( - result -> { - project.add(projectConvertor.ProjectLists(result)); - } - ); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), project); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectLists(projects.getContent())); } @Transactional @@ -354,62 +236,17 @@ public ProjectRes.ProjectLike patchProjectLike(User user, Long projectId) { return new ProjectRes.ProjectLike(!checkProjectLike); } - - public PageResponse> getProjectListQueryDsl(User user, int page, int size, ProjectKind projectKind, String content) { - Pageable pageable = PageRequest.of(page, size); - List project = new ArrayList<>(); - - Page projects = null; - - - if(projectKind == null){ - if(content == null){ - projects = projectRepository.findLoginUserProjectList(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), REPRESENT.getValue(), pageable, ACTIVE.getValue()); - } - else{ - projects = projectRepository.findByContent(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), REPRESENT.getValue(), pageable, ACTIVE.getValue(), content); - - } - }else{ - if(content == null){ - projects = projectRepository.findByProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), - REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue()); - - } - else{ - projects = projectRepository.findByContentAndProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), - REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue(), content); - } - } - - - projects.getContent().forEach( - result -> { - project.add(projectConvertor.ProjectLists(result)); - } - ); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), project); - } - public PageResponse> getTodayProjectLists(User user, int page, int size) { - Pageable pageable = PageRequest.of(page, size); - List project = new ArrayList<>(); + Page projects = projectAdaptor.getTodayProjectLists(user.getId(), page, size); - Page projects = projectRepository.findTodayProject(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), REPRESENT.getValue(), ACTIVE.getValue(), TODAY.getValue(), pageable); - - projects.getContent().forEach( - result -> { - project.add(projectConvertor.ProjectLists(result)); - } - ); - return new PageResponse<>(projects.isLast(), projects.getTotalElements(), project); + return new PageResponse<>(projects.isLast(), projects.getTotalElements(), projectConvertor.convertToProjectLists(projects.getContent())); } public ProjectRes.ProjectAppDetail getProjectAppDetail(User user, Long projectId) { ProjectRepository.ProjectDetail projects = projectRepository.getProjectAppDetail(user.getId(), projectId); List projectImages = projectImageRepository.findByProjectIdOrderBySequenceAsc(projectId); - return projectConvertor.ProjectAppDetail(projects, projectImages); + return projectConvertor.convertToProjectAppDetail(projects, projectImages); } public PageResponse> projectList(User user, int page, int size, ProjectKind projectKind, String content) { @@ -422,7 +259,7 @@ public PageResponse> projectList(User user, int pa projects.getContent().forEach( result -> { - project.add(projectConvertor.ProjectToDto(result)); + project.add(projectConvertor.convertToProjectToDto(result)); } ); @@ -431,13 +268,13 @@ public PageResponse> projectList(User user, int pa public void postComment(User user, Long projectId, ProjectReq.Comment comment) { - projectCommentRepository.save(projectConvertor.Comment(user.getId(), projectId, comment.getComment())); + projectCommentRepository.save(projectConvertor.convertToComment(user.getId(), projectId, comment.getComment())); } public void reportComment(Long commentId, ReportReason reportReason) { ProjectComment projectComment = projectCommentRepository.findByIdAndStatus(commentId, ACTIVE).orElseThrow(()-> new NotFoundException(COMMENT_NOT_EXIST)); - commentReportRepository.save(projectConvertor.ReportComment(commentId, reportReason)); + commentReportRepository.save(projectConvertor.convertToReportComment(commentId, reportReason)); } public void deleteComment(User user, Long commentId) { @@ -446,4 +283,8 @@ public void deleteComment(User user, Long commentId) { projectComment.setStatus(INACTIVE); projectCommentRepository.save(projectComment); } + + public Project checkProjectExists(Long projectId, RegularStatus regularStatus) { + return projectAdaptor.checkRegularProjects(projectId, regularStatus); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/security/JwtAuthenticationEntryPoint.java b/Match-Api/src/main/java/com/example/matchapi/security/JwtAuthenticationEntryPoint.java deleted file mode 100644 index 3e91e67e..00000000 --- a/Match-Api/src/main/java/com/example/matchapi/security/JwtAuthenticationEntryPoint.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.example.matchapi.security; - -import com.example.matchdomain.user.exception.UserAuthErrorCode; -import org.json.JSONException; -import org.json.JSONObject; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.rmi.ServerException; - -@Component -public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, - HttpServletResponse response, - AuthenticationException authException) throws IOException, ServerException { - // 유효한 자격증명을 제공하지 않고 접근하려 할때 401 - String exception = (String) request.getAttribute("exception"); - - UserAuthErrorCode errorCode; - - - /** - * 토큰이 없는 경우 예외처리 - */ - if(exception == null) { - errorCode = UserAuthErrorCode.UNAUTHORIZED_EXCEPTION; - setResponse(response, errorCode); - return; - } - - /** - * 토큰이 만료된 경우 예외처리 - */ - if(exception.equals("NotExistUser")){ - errorCode = UserAuthErrorCode.NOT_EXIST_USER; - setResponse(response, errorCode); - return; - } - else if(exception.equals("ExpiredJwtException")) { - errorCode = UserAuthErrorCode.EXPIRED_JWT_EXCEPTION; - setResponse(response, errorCode); - return; - } - else if (exception.equals("MalformedJwtException")){ - errorCode= UserAuthErrorCode.INVALID_TOKEN_EXCEPTION; - setResponse(response,errorCode); - return; - } - else if(exception.equals("HijackException")){ - errorCode =UserAuthErrorCode.HIJACK_JWT_TOKEN_EXCEPTION; - setResponse(response,errorCode); - return; - } - else if(exception.equals("NoSuchElementException")){ - errorCode = UserAuthErrorCode.NOT_EXISTS_USER_HAVE_TOKEN; - setResponse(response,errorCode); - } - } - - private void setResponse(HttpServletResponse response, UserAuthErrorCode errorCode) throws IOException { - JSONObject json = new JSONObject(); - response.setContentType("application/json;charset=UTF-8"); - response.setCharacterEncoding("utf-8"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - - try { - json.put("code", errorCode.getCode()); - json.put("message", errorCode.getMessage()); - json.put("isSuccess",false); - } catch (JSONException e) { - e.printStackTrace(); - } - - response.getWriter().print(json); - } -} \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java b/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java index 37001bd5..a2546a5b 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/controller/AuthController.java @@ -162,4 +162,11 @@ public CommonResponse checkEmailAuth(@RequestBody UserReq.UserPhoneAuth return CommonResponse.onSuccess("핸드폰 인증 성공"); } + @Operation(summary="01-11🔑 애플로그인 API", description= "애플로그인 API 입니다.") + @PostMapping("/apple") + @ApiErrorCodeExample({UserSignUpErrorCode.class, OtherServerErrorCode.class, RequestErrorCode.class}) + public CommonResponse appleLogin(@RequestBody @Valid UserReq.SocialLoginToken socialLoginToken){ + return CommonResponse.onSuccess(authService.appleLogin(socialLoginToken)); + } + } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java b/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java index dd92b229..51dd9cf8 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/controller/UserController.java @@ -1,6 +1,7 @@ package com.example.matchapi.user.controller; -import com.example.matchapi.security.JwtService; +import com.example.matchapi.common.model.AlarmType; +import com.example.matchapi.common.security.JwtService; import com.example.matchapi.user.dto.UserRes; import com.example.matchapi.user.dto.UserReq; import com.example.matchapi.user.service.UserService; @@ -21,11 +22,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.io.IOException; -import java.util.Optional; import static com.example.matchdomain.user.exception.UserAuthErrorCode.INVALID_REFRESH_TOKEN; @@ -123,9 +125,12 @@ public CommonResponse getProfile( } @Operation(summary = "02-06 프로필 편집 👤 FRAME MY",description = "이미지 파일 변경할 경우 multipart 에 넣어주시고, 이미지 변경 안할 시 multipart null 값으로 보내주세요 아이디는 기존 아이디값+변경할 아이디값 둘중 하나 보내시면 됩니다") - @PatchMapping("/profile") - public CommonResponse modifyUserProfile(@ModelAttribute UserReq.ModifyProfile modifyProfile, @Parameter(hidden = true) @AuthenticationPrincipal User user) throws IOException { - userService.modifyUserProfile(user, modifyProfile); + @PatchMapping(value = "/profile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public CommonResponse modifyUserProfile( + @RequestParam(value = "name", required = false) String name, + @RequestPart(value = "multipartFile", required = false) MultipartFile multipartFile, + @AuthenticationPrincipal User user){ + userService.modifyUserProfile(user, new UserReq.ModifyProfile(name, multipartFile)); return CommonResponse.onSuccess("변경 성공"); } @@ -163,6 +168,20 @@ public CommonResponse modifyEmail( return CommonResponse.onSuccess("변경 성공"); } + @Operation(summary = "02-09 알람 동의 항목 조회 👤",description = "알람 동의 항목 조회 입니다 ACTIVE 필드와 INACTIVE 필드가 있습니다.") + @GetMapping("/alarm") + @ApiErrorCodeExample({UserAuthErrorCode.class}) + public CommonResponse getAlarmAgreeList(@AuthenticationPrincipal User user){ + return CommonResponse.onSuccess(userService.getAlarmAgreeList(user)); + } + + @Operation(summary = "02-10 알람 동의 항목 수정 👤" , description = "알람 동의 항목 수정") + @PatchMapping("/alarm") + @ApiErrorCodeExample({UserAuthErrorCode.class}) + public CommonResponse patchAlarmAgree(@AuthenticationPrincipal User user, + @RequestParam AlarmType alarmType){ + return CommonResponse.onSuccess(userService.patchAlarm(user, alarmType)); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/convertor/UserConvertor.java b/Match-Api/src/main/java/com/example/matchapi/user/convertor/UserConvertor.java index b9d73f03..f924e780 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/convertor/UserConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/convertor/UserConvertor.java @@ -15,6 +15,7 @@ import com.example.matchdomain.user.entity.pk.UserFcmPk; import com.example.matchdomain.user.repository.UserRepository; import com.example.matchinfrastructure.aligo.dto.SendReq; +import com.example.matchinfrastructure.oauth.apple.dto.AppleUserRes; import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserAddressDto; import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserInfoDto; import com.example.matchinfrastructure.oauth.naver.dto.NaverUserInfoDto; @@ -24,6 +25,7 @@ import java.util.List; import static com.example.matchcommon.constants.MatchStatic.BASE_PROFILE; +import static com.example.matchdomain.user.entity.enums.Alarm.ACTIVE; @Convertor @RequiredArgsConstructor @@ -33,7 +35,7 @@ public class UserConvertor { private final PasswordEncoder passwordEncoder; private final AligoProperties aligoProperties; - public User KakaoSignUpUser(KakaoUserInfoDto kakaoUserInfoDto, SocialType authType, Authority authority) { + public User convertToKakaoSignUpUser(KakaoUserInfoDto kakaoUserInfoDto, SocialType authType) { String profileImg = BASE_PROFILE; if(kakaoUserInfoDto.getProfileUrl() != null){ profileImg = kakaoUserInfoDto.getProfileUrl(); @@ -50,17 +52,19 @@ public User KakaoSignUpUser(KakaoUserInfoDto kakaoUserInfoDto, SocialType authTy .birth(authHelper.birthConversion(kakaoUserInfoDto.getBirthYear(), kakaoUserInfoDto.getBirthDay())) .gender(authHelper.genderConversion(kakaoUserInfoDto.getGender())) .role(AuthorityEnum.ROLE_USER.getValue()) - .nickname(kakaoUserInfoDto.getProperties().getNickname()) + .nickname(userHelper.createRandomNickName()) + .serviceAlarm(ACTIVE) + .eventAlarm(ACTIVE) .build(); } - public Authority PostAuthority() { + public Authority convertToPostAuthority() { return Authority.builder() .authorityName("ROLE_USER") .build(); } - public User NaverSignUpUser(NaverUserInfoDto naverUserInfoDto, SocialType authType, Authority authority) { + public User convertToNaverSignUpUser(NaverUserInfoDto naverUserInfoDto, SocialType authType) { String profileImg = BASE_PROFILE; if(naverUserInfoDto.getProfileImage() != null){ profileImg = naverUserInfoDto.getProfileImage(); @@ -77,11 +81,13 @@ public User NaverSignUpUser(NaverUserInfoDto naverUserInfoDto, SocialType authTy .birth(authHelper.birthConversion(naverUserInfoDto.getBirthyear(), naverUserInfoDto.getBirthday())) .gender(authHelper.genderConversion(naverUserInfoDto.getGender())) .role(AuthorityEnum.ROLE_USER.getValue()) - .nickname(naverUserInfoDto.getNickname()) + .nickname(userHelper.createRandomNickName()) + .serviceAlarm(ACTIVE) + .eventAlarm(ACTIVE) .build(); } - public User SignUpUser(UserReq.SignUpUser signUpUser, Authority authority) { + public User convertToSignUpUser(UserReq.SignUpUser signUpUser) { return User.builder() .username(signUpUser.getEmail()) .profileImgUrl(BASE_PROFILE) @@ -93,11 +99,13 @@ public User SignUpUser(UserReq.SignUpUser signUpUser, Authority authority) { .birth(authHelper.birthConversionToLocalDate(signUpUser.getBirthDate())) .gender(signUpUser.getGender()) .role(AuthorityEnum.ROLE_USER.getValue()) - .nickname(signUpUser.getName()) + .nickname(userHelper.createRandomNickName()) + .serviceAlarm(ACTIVE) + .eventAlarm(ACTIVE) .build(); } - public UserRes.EditMyPage toMyPage(User user) { + public UserRes.EditMyPage convertToMyPage(User user) { return UserRes.EditMyPage.builder() .userId(user.getId()) .email(user.getEmail()) @@ -108,7 +116,7 @@ public UserRes.EditMyPage toMyPage(User user) { .build(); } - public UserAddress AddUserAddress(Long userId, KakaoUserAddressDto.ShippingAddresses shippingAddresses) { + public UserAddress convertToAddUserAddress(Long userId, KakaoUserAddressDto.ShippingAddresses shippingAddresses) { return UserAddress.builder() .userId(userId) .name(shippingAddresses.getName()) @@ -123,7 +131,7 @@ public UserAddress AddUserAddress(Long userId, KakaoUserAddressDto.ShippingAddre .build(); } - public RefreshToken RefreshToken(Long userId, String refreshToken, Long refreshTokenSeconds) { + public RefreshToken convertToRefreshToken(Long userId, String refreshToken, Long refreshTokenSeconds) { return RefreshToken.builder() .userId(String.valueOf(userId)) .token(refreshToken) @@ -131,14 +139,14 @@ public RefreshToken RefreshToken(Long userId, String refreshToken, Long refreshT .build(); } - public OrderRes.UserDetail userInfo(User user) { + public OrderRes.UserDetail convertToUserInfo(User user) { return OrderRes.UserDetail.builder() .name(user.getName()) .birthDay(user.getBirth().toString()) .phoneNumber(user.getPhoneNumber()).build(); } - public UserRes.SignUpInfo UserSignUpInfo(Long oneDayUser, Long weekUser, Long monthUser, Long totalUser) { + public UserRes.SignUpInfo convertToUserSignUpInfo(Long oneDayUser, Long weekUser, Long monthUser, Long totalUser) { return UserRes.SignUpInfo.builder() .totalUserCnt(totalUser) .oneDayUserCnt(oneDayUser) @@ -147,7 +155,7 @@ public UserRes.SignUpInfo UserSignUpInfo(Long oneDayUser, Long weekUser, Long mo .build(); } - public UserRes.UserList UserList(UserRepository.UserList result) { + public UserRes.UserList convertToUserList(UserRepository.UserList result) { return UserRes.UserList .builder() .userId(result.getUserId()) @@ -165,7 +173,7 @@ public UserRes.UserList UserList(UserRepository.UserList result) { .build(); } - public UserRes.UserAdminDetail UserAdminDetail(UserRepository.UserList userDetail, List userCards) { + public UserRes.UserAdminDetail convertToUserAdminDetail(UserRepository.UserList userDetail, List userCards) { return UserRes.UserAdminDetail .builder() .userId(userDetail.getUserId()) @@ -195,18 +203,19 @@ public SendReq SendSms(String phone, String code) { .build(); } - public UserRes.Profile UserProfile(User user) { + public UserRes.Profile convertToUserProfile(User user) { return UserRes.Profile .builder() .profileImgUrl(user.getProfileImgUrl()) .name(user.getName()) .socialType(user.getSocialType()) .email(user.getEmail()) + .nickName(user.getNickname()) .phone(user.getPhoneNumber()) .build(); } - public UserFcmToken UserFcm(User user, UserReq.FcmToken token) { + public UserFcmToken convertToUserFcm(User user, UserReq.FcmToken token) { return UserFcmToken.builder() .userFcmPk( UserFcmPk.builder() @@ -216,4 +225,28 @@ public UserFcmToken UserFcm(User user, UserReq.FcmToken token) { .fcmToken(token.getFcmToken()) .build(); } + + public User convertToAppleUserSignUp(AppleUserRes appleUserRes) { + return User.builder() + .username(appleUserRes.getSocialId()) + .password(authHelper.createRandomPassword()) + .profileImgUrl(BASE_PROFILE) + .name(userHelper.createRandomNickName()) + .email(appleUserRes.getEmail()) + .socialId(appleUserRes.getSocialId()) + .socialType(SocialType.APPLE) + .role(AuthorityEnum.ROLE_USER.getValue()) + .nickname(userHelper.createRandomNickName()) + .serviceAlarm(ACTIVE) + .eventAlarm(ACTIVE) + .build(); + } + + public UserRes.AlarmAgreeList convertToAlarmAgree(User user) { + return UserRes.AlarmAgreeList + .builder() + .serviceAlarm(user.getServiceAlarm()) + .eventAlarm(user.getEventAlarm()) + .build(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java b/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java index 85525dbc..d0c35260 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/dto/UserRes.java @@ -1,5 +1,6 @@ package com.example.matchapi.user.dto; +import com.example.matchdomain.user.entity.enums.Alarm; import com.example.matchdomain.user.entity.enums.SocialType; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; @@ -78,7 +79,8 @@ public static class EditMyPage { @NoArgsConstructor @Schema(description ="02-01👤 유저 정보 조회 API Response") public static class MyPage { - private String username; + @Schema(description = "닉네임") + private String name; @Schema(description = "진행중 매치 갯수", required = true, example = "후원 집행 진행 중 갯수") private int underCnt; @@ -94,6 +96,7 @@ public static class MyPage { @Builder @AllArgsConstructor @NoArgsConstructor + @Schema(description = "토큰 재발급", name = "ReIssueToken") public static class ReIssueToken { private String accessToken; } @@ -190,10 +193,23 @@ public static class Profile { private String name; + private String nickName; + private SocialType socialType; private String email;; private String phone; } + + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class AlarmAgreeList { + private Alarm serviceAlarm; + + private Alarm eventAlarm; + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/helper/UserHelper.java b/Match-Api/src/main/java/com/example/matchapi/user/helper/UserHelper.java index 81e0b399..1b726ed9 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/helper/UserHelper.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/helper/UserHelper.java @@ -1,16 +1,31 @@ package com.example.matchapi.user.helper; import com.example.matchcommon.annotation.Helper; +import com.example.matchinfrastructure.user.client.NickNameFeignClient; import lombok.RequiredArgsConstructor; +import javax.annotation.PostConstruct; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @Helper @RequiredArgsConstructor public class UserHelper { + private final NickNameFeignClient nickNameFeignClient; + + private static NickNameFeignClient staticNickNameFeignClient; + + + @PostConstruct + void init() { + staticNickNameFeignClient = this.nickNameFeignClient; + } public String birthConversion(LocalDate birth) { return birth.format(DateTimeFormatter.ofPattern("yyyyMMdd")); } + + public String createRandomNickName(){ + return staticNickNameFeignClient.getNickName().getWords().get(0); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java b/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java index b7990e1e..f5db4ed5 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/service/AuthService.java @@ -1,6 +1,6 @@ package com.example.matchapi.user.service; -import com.example.matchapi.security.JwtService; +import com.example.matchapi.common.security.JwtService; import com.example.matchapi.user.convertor.UserConvertor; import com.example.matchapi.user.dto.UserReq; import com.example.matchapi.user.dto.UserRes; @@ -16,12 +16,17 @@ import com.example.matchdomain.redis.entity.CodeAuth; import com.example.matchdomain.redis.repository.CodeAuthRepository; import com.example.matchdomain.redis.repository.RefreshTokenRepository; +import com.example.matchdomain.user.adaptor.UserAdaptor; import com.example.matchdomain.user.entity.Authority; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.entity.UserAddress; import com.example.matchdomain.user.repository.UserAddressRepository; import com.example.matchdomain.user.repository.UserRepository; import com.example.matchinfrastructure.match_aligo.client.MatchAligoFeignClient; +import com.example.matchinfrastructure.oauth.apple.client.AppleFeignClient; +import com.example.matchinfrastructure.oauth.apple.dto.ApplePublicResponse; +import com.example.matchinfrastructure.oauth.apple.dto.AppleUserRes; +import com.example.matchinfrastructure.oauth.apple.service.AppleAuthService; import com.example.matchinfrastructure.oauth.kakao.client.KakaoFeignClient; import com.example.matchinfrastructure.oauth.kakao.client.KakaoLoginFeignClient; import com.example.matchinfrastructure.oauth.kakao.dto.KakaoLoginTokenRes; @@ -43,8 +48,7 @@ import static com.example.matchcommon.constants.MatchStatic.BEARER; import static com.example.matchdomain.user.entity.enums.AuthorityEnum.ROLE_ADMIN; -import static com.example.matchdomain.user.entity.enums.SocialType.KAKAO; -import static com.example.matchdomain.user.entity.enums.SocialType.NAVER; +import static com.example.matchdomain.user.entity.enums.SocialType.*; import static com.example.matchdomain.user.exception.AdminLoginErrorCode.NOT_ADMIN; import static com.example.matchdomain.user.exception.CodeAuthErrorCode.NOT_CORRECT_AUTH; import static com.example.matchdomain.user.exception.CodeAuthErrorCode.NOT_CORRECT_CODE; @@ -74,6 +78,8 @@ public class AuthService { private final MailService mailService; private final CodeAuthRepository codeAuthRepository; private final MatchAligoFeignClient matchAligoFeignClient; + private final AppleAuthService authService; + private final UserAdaptor userAdaptor; @Transactional @@ -91,7 +97,7 @@ public UserRes.UserToken kakaoLogIn(UserReq.SocialLoginToken socialLoginToken) { if(!kakaoUserAddressDto.isShippingAddressesNeedsAgreement()){ List userAddressList = new ArrayList<>(); for(KakaoUserAddressDto.ShippingAddresses shippingAddresses : kakaoUserAddressDto.getShippingAddresses()){ - UserAddress userAddress = userConvertor.AddUserAddress(userId,shippingAddresses); + UserAddress userAddress = userConvertor.convertToAddUserAddress(userId,shippingAddresses); userAddressList.add(userAddress); } userAddressRepository.saveAll(userAddressList); @@ -111,15 +117,13 @@ public UserRes.UserToken kakaoLogIn(UserReq.SocialLoginToken socialLoginToken) { private UserRes.Token createToken(Long userId) { UserRes.Token token = jwtService.createTokens(userId); - refreshTokenRepository.save(userConvertor.RefreshToken(userId,token.getRefreshToken(),jwtProperties.getRefreshTokenSeconds())); + refreshTokenRepository.save(userConvertor.convertToRefreshToken(userId,token.getRefreshToken(),jwtProperties.getRefreshTokenSeconds())); return token; } private Long kakaoSignUp(KakaoUserInfoDto kakaoUserInfoDto) { - - Authority authority = userConvertor.PostAuthority(); - User user = userConvertor.KakaoSignUpUser(kakaoUserInfoDto, KAKAO, authority); + User user = userConvertor.convertToKakaoSignUpUser(kakaoUserInfoDto, KAKAO); System.out.println(kakaoUserInfoDto.getPhoneNumber()); @@ -128,7 +132,7 @@ private Long kakaoSignUp(KakaoUserInfoDto kakaoUserInfoDto) { @Transactional public Long naverSignUp(NaverUserInfoDto naverUserInfoDto) { - return userRepository.save(userConvertor.NaverSignUpUser(naverUserInfoDto, NAVER, userConvertor.PostAuthority())).getId(); + return userRepository.save(userConvertor.convertToNaverSignUpUser(naverUserInfoDto, NAVER)).getId(); } public KakaoLoginTokenRes getOauthToken(String code, String referer) { return kakaoLoginFeignClient.kakaoAuth( @@ -171,9 +175,7 @@ public UserRes.UserToken signUpUser(UserReq.SignUpUser signUpUser) { if(userRepository.existsByPhoneNumber(signUpUser.getPhone())) throw new BadRequestException(USERS_EXISTS_PHONE); if(userRepository.existsByEmail(signUpUser.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); - Authority authority = userConvertor.PostAuthority(); - - Long userId = userRepository.save(userConvertor.SignUpUser(signUpUser,authority)).getId(); + Long userId = userRepository.save(userConvertor.convertToSignUpUser(signUpUser)).getId(); UserRes.Token token = createToken(userId); @@ -254,4 +256,24 @@ public void checkPhoneAuth(UserReq.UserPhoneAuth phone) { CodeAuth codeAuth = codeAuthRepository.findById(phone.getPhone()).orElseThrow(()->new BadRequestException(NOT_CORRECT_AUTH)); if(!codeAuth.getCode().equals(phone.getCode()))throw new BadRequestException(NOT_CORRECT_CODE); } + + public UserRes.UserToken appleLogin(UserReq.SocialLoginToken socialLoginToken) { + AppleUserRes appleUserRes = authService.appleLogin(socialLoginToken.getAccessToken()); + + if(userRepository.existsByEmail(appleUserRes.getEmail())) throw new BadRequestException(USERS_EXISTS_EMAIL); + Optional user = userAdaptor.existsSocialUser(appleUserRes.getSocialId(), APPLE); + + Long userId; + if (user.isEmpty()) userId = appleSignUp(appleUserRes); + + else userId = user.get().getId(); + + UserRes.Token token = createToken(userId); + + return new UserRes.UserToken(userId, token.getAccessToken(), token.getRefreshToken()); + } + + private Long appleSignUp(AppleUserRes appleUserRes) { + return userRepository.save(userConvertor.convertToAppleUserSignUp(appleUserRes)).getId(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java b/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java index 127d376f..d68022c8 100644 --- a/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java +++ b/Match-Api/src/main/java/com/example/matchapi/user/service/UserService.java @@ -1,5 +1,6 @@ package com.example.matchapi.user.service; +import com.example.matchapi.common.model.AlarmType; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.service.OrderService; import com.example.matchapi.project.convertor.ProjectConvertor; @@ -16,6 +17,7 @@ import com.example.matchdomain.project.repository.ProjectUserAttentionRepository; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.entity.UserAddress; +import com.example.matchdomain.user.entity.enums.Alarm; import com.example.matchdomain.user.entity.pk.UserFcmPk; import com.example.matchdomain.user.exception.ModifyEmailCode; import com.example.matchdomain.user.repository.UserAddressRepository; @@ -36,7 +38,10 @@ import java.util.List; import java.util.Optional; +import static com.example.matchapi.common.model.AlarmType.EVENT; import static com.example.matchcommon.constants.MatchStatic.*; +import static com.example.matchdomain.user.entity.enums.Alarm.ACTIVE; +import static com.example.matchdomain.user.entity.enums.Alarm.INACTIVE; import static com.example.matchdomain.user.exception.ModifyEmailCode.NOT_CORRECT_EMAIL; import static com.example.matchdomain.user.exception.ModifyPhoneErrorCode.NOT_CORRECT_PHONE; import static com.example.matchdomain.user.exception.UserNormalSignUpErrorCode.USERS_EXISTS_PHONE; @@ -48,9 +53,7 @@ public class UserService { private final UserRepository userRepository; private final UserAddressRepository userAddressRepository; private final UserConvertor userConvertor; - private final DonationUserRepository donationUserRepository; private final ProjectConvertor projectConvertor; - private final ProjectHelper projectHelper; private final ProjectUserAttentionRepository projectUserAttentionRepository; private final OrderService orderService; private final RegularPaymentRepository regularPaymentRepository; @@ -68,31 +71,31 @@ public List findUserAddress(Long id) { } public UserRes.EditMyPage getEditMyPage(User user) { - return userConvertor.toMyPage(user); + return userConvertor.convertToMyPage(user); } public UserRes.MyPage getMyPage(User user) { List regularPayments = regularPaymentRepository.findByUser(user); Long projectAttentionCnt = projectUserAttentionRepository.countById_userId(user.getId()); - return projectConvertor.getMyPage(regularPayments,projectAttentionCnt, user.getName()); + return projectConvertor.getMyPage(regularPayments,projectAttentionCnt, user.getNickname()); } public OrderRes.UserDetail getUserInfo(User user) { - return userConvertor.userInfo(user); + return userConvertor.convertToUserInfo(user); } public UserRes.SignUpInfo getUserSignUpInfo() { LocalDate localDate = LocalDate.now(); - LocalDateTime localDateTime = LocalDateTime.now(); Long totalUser = userRepository.countBy(); Long oneDayUser = userRepository.countByCreatedAtGreaterThanAndCreatedAtLessThan(LocalDateTime.parse(localDate+FIRST_TIME), LocalDateTime.parse(localDate+LAST_TIME)); Long weekUser = userRepository.countByCreatedAtGreaterThanAndCreatedAtLessThan(LocalDateTime.parse(localDate.minusWeeks(1)+FIRST_TIME) , LocalDateTime.parse(localDate+LAST_TIME)); Long monthUser = userRepository.countByCreatedAtGreaterThanAndCreatedAtLessThan(LocalDateTime.parse(localDate.with(TemporalAdjusters.firstDayOfMonth())+FIRST_TIME), LocalDateTime.parse(localDate.with(TemporalAdjusters.lastDayOfMonth())+LAST_TIME)); - return userConvertor.UserSignUpInfo(oneDayUser,weekUser,monthUser,totalUser); + return userConvertor.convertToUserSignUpInfo(oneDayUser,weekUser,monthUser,totalUser); } + @Transactional public PageResponse> getUserList(int page, int size, Status status, String content) { Pageable pageable = PageRequest.of(page, size); Page userList = null; @@ -113,7 +116,7 @@ else if(status!=null){ userList.getContent().forEach( result -> userLists.add( - userConvertor.UserList(result) + userConvertor.convertToUserList(result) ) ); @@ -126,13 +129,14 @@ public UserRes.UserAdminDetail getUserAdminDetail(Long userId) { List userCards = orderService.getUserBillCard(userId); - return userConvertor.UserAdminDetail(userDetail,userCards); + return userConvertor.convertToUserAdminDetail(userDetail,userCards); } public UserRes.Profile getProfile(User user) { - return userConvertor.UserProfile(user); + return userConvertor.convertToUserProfile(user); } + @Transactional public void modifyUserProfile(User user, UserReq.ModifyProfile modifyProfile) { if(modifyProfile.getName() == null && modifyProfile.getMultipartFile()!=null){ String beforeProfileImg = user.getProfileImgUrl(); @@ -143,7 +147,8 @@ public void modifyUserProfile(User user, UserReq.ModifyProfile modifyProfile) { user.setProfileImgUrl(newProfileImg); } else if(modifyProfile.getMultipartFile() == null && modifyProfile.getName()!=null){ - user.setName(modifyProfile.getName()); + System.out.println("유저 닉네임 편집"); + user.setNickname(modifyProfile.getName()); } else if (modifyProfile.getMultipartFile() != null){ String beforeProfileImg = user.getProfileImgUrl(); @@ -158,7 +163,7 @@ else if (modifyProfile.getMultipartFile() != null){ } public void saveFcmToken(User user, UserReq.FcmToken token) { - userFcmTokenRepository.save(userConvertor.UserFcm(user, token)); + userFcmTokenRepository.save(userConvertor.convertToUserFcm(user, token)); } public void deleteFcmToken(Long userId, String deviceId) { @@ -181,4 +186,33 @@ public void modifyEmail(User user, UserReq.ModifyEmail email) { user.setEmail(email.getNewEmail()); userRepository.save(user); } + + public UserRes.AlarmAgreeList getAlarmAgreeList(User user) { + System.out.println(user.getName()); + return userConvertor.convertToAlarmAgree(user); + } + + public UserRes.AlarmAgreeList patchAlarm(User user, AlarmType alarmType) { + if(alarmType.equals(EVENT)){ + Alarm alarm = user.getEventAlarm(); + if(alarm == ACTIVE){ + user.setEventAlarm(INACTIVE); + } + else{ + user.setEventAlarm(ACTIVE); + } + }else{ + Alarm alarm = user.getServiceAlarm(); + if(alarm == ACTIVE){ + user.setServiceAlarm(INACTIVE); + } + else{ + user.setServiceAlarm(ACTIVE); + } + } + + user = userRepository.save(user); + + return userConvertor.convertToAlarmAgree(user); + } } diff --git a/Match-Api/src/main/resources/application.yml b/Match-Api/src/main/resources/application.yml index b4d6db11..54412c78 100644 --- a/Match-Api/src/main/resources/application.yml +++ b/Match-Api/src/main/resources/application.yml @@ -4,6 +4,9 @@ server: context-path: / spring: + cache: + ehcache: + config: classpath:ehcache.xml thymeleaf: prefix: classpath:/templates/ suffix: .html @@ -31,7 +34,8 @@ springdoc: swagger-ui: path: demo-ui.html # Swagger UI 경로 => localhost:8000/demo-ui.html tags-sorter: alpha # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 - operations-sorter: summary # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 + operations-sorter: alpha # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 + doc-expansion: none api-docs: path: /api-docs/json groups: diff --git a/Match-Api/src/main/resources/ehcache.xml b/Match-Api/src/main/resources/ehcache.xml new file mode 100644 index 00000000..682e1c0f --- /dev/null +++ b/Match-Api/src/main/resources/ehcache.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Match-Api/src/main/resources/templates/Marketing.html b/Match-Api/src/main/resources/templates/Marketing.html new file mode 100644 index 00000000..842fe848 --- /dev/null +++ b/Match-Api/src/main/resources/templates/Marketing.html @@ -0,0 +1,10 @@ + + + + + 마케팅 정보 수집 및 수신 동의 + + +

마케팅 정보 수집 및 수신 동의

+ + \ No newline at end of file diff --git a/Match-Api/src/main/resources/templates/PrivacyPolicy.html b/Match-Api/src/main/resources/templates/PrivacyPolicy.html new file mode 100644 index 00000000..6cd72242 --- /dev/null +++ b/Match-Api/src/main/resources/templates/PrivacyPolicy.html @@ -0,0 +1,164 @@ + + + + + + 개인정보 처리지침 + + + +

개인정보 처리지침

+ +

제1조 (개인정보의 처리목적)

+

회사는 다음의 목적을 위하여 개인정보를 처리합니다. 처리하고 있는 개인정보는 다음의 목적 이외의 용도로는 이용되지 않으며, 이용 목적이 변경되는 경우에는 개인정보보호법 제18조에 따라 별도의 동의를 받는 등 필요한 조치를 이행할 예정입니다.

+ +
    +
  1. 홈페이지 회원 가입 및 관리
    회원 가입 의사 확인, 회원제 서비스 제공에 따른 본인 식별․인증, 회원자격 유지․관리, 제한적 본인확인제 시행에 따른 본인확인, 서비스 부정 이용 방지, 만 14세 미만 아동의 개인정보처리 시 법정대리인의 동의 여부 확인, 각종 고지․통지, 고충 처리 등을 목적으로 개인정보를 처리합니다.
  2. +
  3. 재화 또는 서비스 제공
    물품 배송, 서비스 제공, 계약서 및 청구서 발송, 콘텐츠 제공, 맞춤서비스 제공, 본인인증, 연령인증, 요금 결제 및 정산, 채권추심 등을 목적으로 개인정보를 처리합니다.
  4. +
  5. 고충 처리
    민원인의 신원 확인, 민원사항 확인, 사실조사를 위한 연락․통지, 처리 결과 통보 등의 목적으로 개인정보를 처리합니다.
  6. +
+ +

제2조 (개인정보의 처리 및 보유기간)

+

① 회사는 법령에 따른 개인정보 보유, 이용 기간 또는 정보주체로부터 개인정보를 수집 시에 동의 받은 개인정보 보유, 이용 기간 내에서 개인정보를 처리, 보유합니다.

+

② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다.

+
    +
  1. 홈페이지 회원 가입 및 관리 : 사업자/단체 홈페이지 탈퇴 시까지
    + 다만, 다음의 사유에 해당하는 경우에는 해당 사유 종료 시까지 +
      +
    • 관계 법령 위반에 따른 수사, 조사 등이 진행 중인 경우에는 해당 수사, 조사 종료 시까지
    • +
    • 홈페이지 이용에 따른 채권 및 채무관계 잔존 시에는 해당 채권, 채무 관계 정산 시까지
    • +
    +
  2. +
+ +

제3조 (개인정보의 제3자 제공)

+

① 회사는 정보주체의 개인정보를 제1조(개인정보의 처리목적)에서 명시한 범위 내에서만 처리하며, 정보주체의 동의, 법률의 특별한 규정 등 개인정보 보호법 제17조에 해당하는 경우에만 개인정보를 제3자에게 제공합니다.

+

② 회사는 다음과 같이 개인정보를 제3자에게 제공하고 있습니다.

+
    +
  • 개인정보를 제공받는 자 : (주) OOO 카드
  • +
  • 제공받는 자의 개인정보 이용목적 : 이벤트 공동개최 등 업무제휴 및 제휴 신용카드 발급
  • +
  • 제공하는 개인정보 항목 : 성명, 주소, 전화번호, 이메일주소, 카드결제계좌정보
  • +
  • 제공받는 자의 보유, 이용기간 : 신용카드 발급계약에 따른 거래기간동안
  • +
+ +

제4조 (개인정보처리의 위탁)

+

① 회사는 원활한 개인정보 업무처리를 위하여 다음과 같이 개인정보 처리업무를 위탁하고 있습니다.

+
    +
  1. 전화 상담 센터 운영
    + - 위탁받는 자 (수탁자) : MATCH CS센터
    + - 위탁하는 업무의 내용 : 전화상담 응대, 부서 및 직원 안내 등
  2. +
+

② 회사는 위탁계약 체결 시 개인정보 보호법 제25조에 따라 위탁업무 수행목적 외 개인정보 처리금지, 기술적․관리적 보호조치, 재위탁 제한, 수탁자에 대한 관리․감독, 손해배상 등 책임에 관한 사항을 계약서 등 문서에 명시하고, 수탁자가 개인정보를 안전하게 처리하는지를 감독하고 있습니다.

+

③ 위탁업무의 내용이나 수탁자가 변경될 경우에는 지체없이 본 개인정보 처리방침을 통하여 공개하도록 하겠습니다.

+ +

제5조 (이용자 및 법정대리인의 권리와 그 행사 방법)

+

① 정보주체는 회사에 대해 언제든지 다음 각 호의 개인정보 보호 관련 권리를 행사할 수 있습니다.

+
    +
  1. 개인정보 열람 요구
  2. +
  3. 오류 등이 있을 경우 정정 요구
  4. +
  5. 삭제요구
  6. +
  7. 처리정지 요구
  8. +
+

② 제1항에 따른 권리 행사는 회사에 대해 서면, 전화, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 회사는 이에 대해 지체없이 조치하겠습니다.

+

③ 정보주체가 개인정보의 오류 등에 대한 정정 또는 삭제를 요구한 경우에는 회사는 정정 또는 삭제를 완료할 때까지 당해 개인정보를 이용하거나 제공하지 않습니다.

+

④ 제1항에 따른 권리 행사는 정보주체의 법정대리인이나 위임을 받은 자 등 대리인을 통하여 하실 수 있습니다. 이 경우 개인정보 보호법 시행규칙 별지 제11호 서식에 따른 위임장을 제출하셔야 합니다.

+

⑤ 정보주체는 개인정보 보호법 등 관계 법령을 위반하여 회사가 처리하고 있는 정보주체 본인이나 타인의 개인정보 및 사생활을 침해하여서는 아니 됩니다.

+ +

제6조 (처리하는 개인정보 항목)

+

회사는 다음의 개인정보 항목을 처리하고 있습니다.

+
    +
  1. 홈페이지 회원 가입 및 관리
    + 필수항목 : 성명, 생년월일, 아이디, 비밀번호, 주소, 전화번호, 성별, 이메일주소, 아이핀번호
    + 선택항목 : 결혼 여부, 관심 분야
  2. +
  3. 재화 또는 서비스 제공
    + 필수항목 : 성명, 생년월일, 아이디, 비밀번호, 주소, 전화번호, 이메일주소, 아이핀번호, 신용카드번호, 은행계좌정보 등 결제정보
    + 선택항목 : 관심분야, 과거 구매내역
  4. +
  5. 인터넷 서비스 이용과정에서 아래 개인정보 항목이 자동으로 생성되어 수집될 수 있습니다.
    + IP주소, 쿠키, MAC주소, 서비스 이용기록, 방문기록, 불량 이용기록 등
  6. +
+ +

제7조 (개인정보의 파기)

+

① 회사는 개인정보 보유 기간의 경과, 처리목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체없이 해당 개인정보를 파기합니다.

+

② 정보주체로부터 동의받은 개인정보 보유 기간이 경과하거나 처리목적이 달성되었음에도 불구하고 다른 법령에 따라 개인정보를 계속 보존하여야 하는 경우에는, 해당 개인정보를 별도의 데이터베이스(DB)로 옮기거나 보관장소를 달리하여 보존합니다.

+

③ 개인정보 파기의 절차 및 방법은 다음과 같습니다.

+
    +
  1. 파기 절차
    + 회사는 파기 사유가 발생한 개인정보를 선정하고, 회사의 개인정보 보호책임자의 승인을 받아 개인정보를 파기합니다.
  2. +
  3. 파기 방법
    + 회사는 전자적 파일 형태로 기록․저장된 개인정보는 기록을 재생할 수 없도록 로우레벨포맷(Low Level Format) 등의 방법을 이용하여 파기하며, 종이 문서에 기록․저장된 개인정보는 분쇄기로 분쇄하거나 소각하여 파기합니다.
  4. +
+ +

제8조 (개인정보의 안전성 확보조치)

+

회사는 개인정보의 안전성 확보를 위해 다음과 같은 조치를 하고 있습니다.

+
    +
  1. 관리적 조치 : 내부관리계획 수립 및 시행, 정기적 직원 교육 등
  2. +
  3. 기술적 조치 : 개인정보처리시스템 등의 접근 권한 관리, 접근통제시스템 설치, 고유 식별정보 등의 암호화, 보안프로그램 설치
  4. +
  5. 물리적 조치 : 전산실, 자료보관실 등의 접근통제
  6. +
+ +

제9조 (개인정보 자동 수집 장치의 설치∙운영 및 거부에 관한 사항)

+

① 회사는 이용자에게 개별적인 맞춤 서비스를 제공하기 위해 이용정보를 저장하고 수시로 불러오는 ‘쿠키(cookie)’를 사용합니다.

+

② 쿠키는 웹사이트를 운영하는데 이용되는 서버(http)가 이용자의 컴퓨터 브라우저에 보내는 소량의 정보이며 이용자들의 컴퓨터 내의 하드디스크에 저장되기도 합니다.

+
    +
  1. 쿠키의 사용 목적: 이용자가 방문한 각 서비스와 웹 사이트들에 대한 방문 및 이용형태, 인기 검색어, 보안접속 여부, 등을 파악하여 이용자에게 최적화된 정보 제공을 위해 사용됩니다.
  2. +
  3. 쿠키의 설치∙운영 및 거부 : 웹브라우저 상단의 도구>인터넷 옵션>개인정보 메뉴의 옵션 설정을 통해 쿠키 저장을 거부 할 수 있습니다.
  4. +
  5. 쿠키 저장을 거부할 경우 맞춤형 서비스 이용에 어려움이 발생할 수 있습니다.
  6. +
+ +

제10조 (개인정보 보호책임자)

+

① 회사는 개인정보 처리에 관한 업무를 총괄해서 책임지고, 개인정보 처리와 관련한 정보주체의 불만 처리 및 피해구제 등을 위하여 아래와 같이 개인정보 보호책임자를 지정하고 있습니다.

+

▶ 개인정보 보호책임자

+ +

※ 개인정보 보호 담당부서로 연결됩니다.

+

▶ 개인정보 보호 담당부서

+ +

② 정보주체께서는 회사의 서비스(또는 사업)을 이용하시면서 발생한 모든 개인정보 보호 관련 문의, 불만 처리, 피해구제 등에 관한 사항을 개인정보 보호책임자 및 담당부서로 문의하실 수 있습니다. 회사는 정보주체의 문의에 대해 지체없이 답변 및 처리해드릴 것입니다.

+ +

제11조 (개인정보 열람청구)

+

정보주체는 개인정보 보호법 제35조에 따른 개인정보의 열람 청구를 아래의 부서에 할 수 있습니다. 회사는 정보주체의 개인정보 열람 청구가 신속하게 처리되도록 노력하겠습니다.

+

▶ 개인정보 열람청구 접수․처리 부서

+ + +

제12조 (권익침해 구제 방법)

+

정보주체는 아래의 기관에 대해 개인정보 침해에 대한 피해구제, 상담 등을 문의하실 수 있습니다.

+

▶ 개인정보 침해신고센터 (한국인터넷진흥원 운영)

+
    +
  • 소관 업무 : 개인정보 침해사실 신고, 상담 신청
  • +
  • 홈페이지 : privacy.kisa.or.kr
  • +
  • 전화 : (국번없이) 118
  • +
  • 주소 : (58324) 전남 나주시 진흥길 9(빛가람동 301-2) 3층 개인정보침해신고센터
  • +
+

▶ 개인정보 분쟁조정위원회

+
    +
  • 소관 업무 : 개인정보 분쟁조정 신청, 집단분쟁조정 (민사적 해결)
  • +
  • 홈페이지 : www.kopico.go.kr
  • +
  • 전화 : (국번없이) 1833-6972
  • +
  • 주소 : (03171) 서울특별시 종로구 세종대로 209 정부서울청사 4층
  • +
+

▶ 대검찰청 과학수사부 (사이버수사과)

+
    +
  • 소관 업무 : 개인정보 침해사실 신고, 수사 의뢰
  • +
  • 홈페이지 : www.spo.go.kr
  • +
  • 전화 : (국번없이) 1301
  • +
  • 주소 : (335-804) 대전광역시 유성구 대학로 101 대검찰청 과학수사부 (사이버수사과)
  • +
+ +

제13조 (개인정보 처리방침 변경)

+

① 이 개인정보 처리방침은 시행일로부터 적용되며, 법령 및 방침에 따른 변경내용의 추가, 삭제 및 정정이 있는 경우에는 변경사항의 시행 7일 전부터 공지사항을 통하여 고지할 것입니다.

+

② 이 개인정보 처리방침은 2022년 1월 1일부터 적용됩니다.

+ + + + diff --git a/Match-Api/src/main/resources/templates/TermsConditions.html b/Match-Api/src/main/resources/templates/TermsConditions.html new file mode 100644 index 00000000..30034e6b --- /dev/null +++ b/Match-Api/src/main/resources/templates/TermsConditions.html @@ -0,0 +1,126 @@ + + + + + 서비스 이용약관 + + +

서비스 이용약관

+ +

제1조 목적

+

본 개인정보 처리방침은 “우리가 바라온 투명한 기부 플랫폼 MATCH | 매치 플랫폼 공식사이트”(이하 "사이트")의 서비스의 이용조건과 운영에 관한 제반 사항 규정을 목적으로 합니다.

+ +

제2조 용어의 정의

+

본 약관에서 사용되는 주요한 용어의 정의는 다음과 같습니다.

+
    +
  1. 회원: 사이트의 약관에 동의하고 개인정보를 제공하여 회원등록을 한 자로서, 사이트와의 이용계약을 체결하고 사이트를 이용하는 이용자를 말합니다.
  2. +
  3. 이용계약: 사이트 이용과 관련하여 사이트와 회원간에 체결하는 계약을 말합니다.
  4. +
  5. 회원 아이디(이하 "ID"): 회원의 식별과 회원의 서비스 이용을 위하여 회원별로 부여하는 고유한 문자와 숫자의 조합을 말합니다.
  6. +
  7. 비밀번호: 회원이 부여받은 ID와 일치된 회원임을 확인하고 회원의 권익 보호를 위하여 회원이 선정한 문자와 숫자의 조합을 말합니다.
  8. +
  9. 운영자: 서비스에 홈페이지를 개설하여 운영하는 운영자를 말합니다.
  10. +
  11. 해지: 회원이 이용계약을 해약하는 것을 말합니다.
  12. +
+ +

제3조 약관 외 준칙

+

운영자는 필요한 경우 별도로 운영정책을 공지 안내할 수 있으며, 본 약관과 운영정책이 중첩될 경우 운영정책이 우선 적용됩니다.

+ +

제4조 이용계약 체결

+

이용계약은 회원으로 등록하여 사이트를 이용하려는 자의 본 약관 내용에 대한 동의와 가입신청에 대하여 운영자의 이용승낙으로 성립합니다.

+ +

제5조 서비스 이용 신청

+
    +
  1. 회원으로 등록하여 사이트를 이용하려는 이용자는 사이트에서 요청하는 제반정보(이용자ID,비밀번호, 닉네임 등)를 제공해야 합니다.
  2. +
  3. 타인의 정보를 도용하거나 허위의 정보를 등록하는 등 본인의 진정한 정보를 등록하지 않은 회원은 사이트 이용과 관련하여 아무런 권리를 주장할 수 없으며, 관계 법령에 따라 처벌받을 수 있습니다.
  4. +
+ +

제6조 개인정보처리방침

+

사이트 및 운영자는 회원가입 시 제공한 개인정보 중 비밀번호를 가지고 있지 않으며 이와 관련된 부분은 사이트의 개인정보처리방침을 따릅니다.

+

운영자는 관계 법령이 정하는 바에 따라 회원등록정보를 포함한 회원의 개인정보를 보호하기 위하여 노력합니다.

+

회원의 개인정보보호에 관하여 관계법령 및 사이트가 정하는 개인정보처리방침에 정한 바에 따릅니다.

+

단, 회원의 귀책 사유로 인해 노출된 정보에 대해 운영자는 일체의 책임을 지지 않습니다.

+

운영자는 회원이 미풍양속에 저해되거나 국가안보에 위배되는 게시물 등 위법한 게시물을 등록 · 배포할 경우 관련 기관의 요청이 있을 시 회원의 자료를 열람 및 해당 자료를 관련 기관에 제출할 수 있습니다.

+ +

제7조 운영자의 의무

+
    +
  1. 운영자는 이용회원으로부터 제기되는 의견이나 불만이 정당하다고 인정할 경우에는 가급적 빨리 처리하여야 합니다. 다만, 개인적인 사정으로 신속한 처리가 곤란한 경우에는 사후에 공지 또는 이용회원에게 쪽지, 전자우편 등을 보내는 등 최선을 다합니다.
  2. +
  3. 운영자는 계속적이고 안정적인 사이트 제공을 위하여 설비에 장애가 생기거나 유실된 때에는 이를 지체 없이 수리 또는 복구할 수 있도록 사이트에 요구할 수 있습니다. 다만, 천재지변 또는 사이트나 운영자에 부득이한 사유가 있는 경우, 사이트 운영을 일시 정지할 수 있습니다.
  4. +
+ +

제8조 회원의 의무

+
    +
  1. 회원은 본 약관에서 규정하는 사항과 운영자가 정한 제반 규정, 공지사항 및 운영정책 등 사이트가 공지하는 사항 및 관계 법령을 준수하여야 하며, 기타 사이트의 업무에 방해가 되는 행위, 사이트의 명예를 손상하는 행위를 해서는 안 됩니다.
  2. +
  3. 회원은 사이트의 명시적 동의가 없는 한 서비스의 이용 권한, 기타 이용계약상 지위를 타인에게 양도, 증여할 수 없으며, 이를 담보로 제공할 수 없습니다.
  4. +
  5. 이용고객은 아이디 및 비밀번호 관리에 상당한 주의를 기울여야 하며, 운영자나 사이트의 동의 없이 제3자에게 아이디를 제공하여 이용하게 할 수 없습니다.
  6. +
  7. 회원은 운영자와 사이트 및 제3자의 지적 재산권을 침해해서는 안 됩니다.
  8. +
+ +

제9조 서비스 이용 시간

+
    +
  1. 서비스 이용 시간은 업무상 또는 기술상 특별한 지장이 없는 한 연중무휴 1일 24시간을 원칙으로 합니다. 단, 사이트는 시스템 정기점검, 증설 및 교체를 위해 사이트가 정한 날이나 시간에 서비스를 일시중단 할 수 있으며 예정된 작업으로 인한 서비스 일시 중단은 사이트의 홈페이지에 사전에 공지하오니 수시로 참고하시길 바랍니다.
  2. +
  3. 단, 사이트는 다음 경우에 대하여 사전 공지나 예고 없이 서비스를 일시적 혹은 영구적으로 중단할 수 있습니다.
  4. +
+ +

제10조 서비스 이용 해지

+
    +
  1. 회원이 사이트와의 이용계약을 해지하고자 하는 경우에는 회원 본인이 온라인을 통하여 등록해지 신청을 하여야 합니다. 한편, 사이트 이용 해지와 별도로 사이트에 대한 이용계약 해지는 별도로 하셔야 합니다.
  2. +
  3. 해지 신청과 동시에 사이트가 제공하는 사이트 관련 프로그램이 회원 관리 화면에서 자동적으로 삭제됨으로 운영자는 더 이상 해지신청자의 정보를 볼 수 없습니다.
  4. +
+ +

제11조 서비스 이용 제한

+

회원은 다음 각호에 해당하는 행위를 하여서는 아니 되며 해당 행위를 한 경우에 사이트는 회원의 서비스 이용 제한 및 적법한 조치를 할 수 있으며 이용계약을 해지하거나 기간을 정하여 서비스를 중지할 수 있습니다.

+
    +
  1. 회원 가입시 혹은 가입 후 정보 변경 시 허위 내용을 등록하는 행위
  2. +
  3. 타인의 사이트 이용을 방해하거나 정보를 도용하는 행위
  4. +
  5. 사이트의 운영진, 직원 또는 관계자를 사칭하는 행위
  6. +
  7. 사이트, 기타 제3자의 인격권 또는 지적재산권을 침해하거나 업무를 방해하는 행위
  8. +
  9. 다른 회원의 ID를 부정하게 사용하는 행위
  10. +
  11. 다른 회원에 대한 개인정보를 그 동의 없이 수집, 저장, 공개하는 행위
  12. +
  13. 범죄와 결부된다고 객관적으로 판단되는 행위
  14. +
  15. 기타 관련 법령에 위배되는 행위
  16. +
+ +

제12조 게시물의 관리

+
    +
  1. 사이트의 게시물과 자료의 관리 및 운영의 책임은 운영자에게 있습니다. 운영자는 항상 불량 게시물 및 자료에 대하여 모니터링을 하여야 하며, 불량 게시물 및 자료를 발견하거나 신고를 받으면 해당 게시물 및 자료를 삭제하고 이를 등록한 회원에게 주의를 주어야 합니다.
  2. +
  3. 한편, 이용회원이 올린 게시물에 대해서는 게시자 본인에게 책임이 있으니 회원 스스로 본 이용약관에서 위배되는 게시물은 게재해서는 안 됩니다.
  4. +
  5. 정보통신윤리위원회 등 공공기관의 시정요구가 있는 경우 운영자는 회원의 사전동의 없이 게시물을 삭제하거나 이동 할 수 있습니다.
  6. +
  7. 불량게시물의 판단기준은 다음과 같습니다. +
      +
    • 다른 회원 또는 제3자에게 심한 모욕을 주거나 명예를 손상하는 내용인 경우
    • +
    • 공공질서 및 미풍양속에 위반되는 내용을 유포하거나 링크 시키는 경우
    • +
    • 불법 복제 또는 해킹을 조장하는 내용인 경우
    • +
    • 영리를 목적으로 하는 광고일 경우
    • +
    • 범죄와 결부된다고 객관적으로 인정되는 내용일 경우
    • +
    • 다른 이용자 또는 제3자와 저작권 등 기타 권리를 침해하는 경우
    • +
    • 기타 관계 법령에 위배된다고 판단되는 경우
    • +
    +
  8. +
  9. 사이트 및 운영자는 게시물 등에 대하여 제3자로부터 명예훼손, 지적재산권 등의 권리 침해를 이유로 게시중단 요청을 받은 경우 이를 임시로 게시중단(전송중단)할 수 있으며, 게시중단 요청자와 게시물 등록자 간에 소송, 합의 기타 이에 준하는 관련 기관의 결정 등이 이루어져 사이트에 접수된 경우 이에 따릅니다.
  10. +
+ +

제13조 게시물의 보관

+

사이트 운영자가 불가피한 사정으로 본 사이트를 중단하게 될 경우, 회원에게 사전 공지를 하고 게시물의 이전이 쉽도록 모든 조치를 하기 위해 노력합니다.

+ +

제14조 게시물에 대한 저작권

+
    +
  1. 회원이 사이트 내에 게시한 게시물의 저작권은 게시한 회원에게 귀속됩니다. 또한 사이트는 게시자의 동의 없이 게시물을 상업적으로 이용할 수 없습니다. 다만 비영리 목적인 경우는 그러하지 아니하며, 또한 서비스 내의 게재권을 갖습니다.
  2. +
  3. 회원은 서비스를 이용하여 취득한 정보를 임의 가공, 판매하는 행위 등 서비스에 게재된 자료를 상업적으로 사용할 수 없습니다.
  4. +
  5. 운영자는 회원이 게시하거나 등록하는 사이트 내의 내용물, 게시 내용에 대해 제12조 각호에 해당한다고 판단되는 경우 사전통지 없이 삭제하거나 이동 또는 등록 거부할 수 있습니다.
  6. +
+ +

제15조 손해배상

+
    +
  1. 본 사이트의 발생한 모든 민, 형법상 책임은 회원 본인에게 1차적으로 있습니다.
  2. +
  3. 본 사이트로부터 회원이 받은 손해가 천재지변, 전쟁, 기간통신 사고, 해결이 곤란한 기술적 결함 기타 비상사태 등 불가항력적인 경우에는 책임이 면제됩니다.
  4. +
+ +

제16조 면책 조항

+

운영자는 회원의 노력과 운영자의 지식과 경험에 따라 사이트를 제공하지만 사이트에 대한 특별한 하자가 없는 이상, 신뢰성이나 안정성이 없을 수 있습니다. 그러므로 사이트 및 운영자는 면책됩니다.

+ +

제17조 관할 법원

+

사이트 이용 중 발생한 분쟁에 대해 소송이 제기될 경우 관할 법원은 운영자 소재지의 법원을 합니다.

+ +

이상으로 개인정보 처리방침과 이용약관을 HTML 형식으로 수정한 내용입니다. 원하는 부분을 추가, 삭제 또는 수정하여 웹사이트에 게시하거나 이메일로 전송할 수 있습니다. 개인정보 처리방침과 이용약관은 법률적인 요구를 준수하고 사용자에게 제공하는 서비스와 웹사이트의 운영에 중요한 역할을 합니다.

+ + + \ No newline at end of file diff --git a/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java b/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java index d83d6cfa..e00a7ae9 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java @@ -7,6 +7,7 @@ import com.example.matchdomain.donation.entity.enums.PayMethod; import com.example.matchdomain.donation.entity.enums.PaymentStatus; import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.donation.entity.flameEnum.FlameImage; import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkRequest; import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkResponse; import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; @@ -43,6 +44,7 @@ public DonationUser donationUser(PortOneBillPayResponse response, Long userId, S .regularStatus(RegularStatus.REGULAR) .projectId(projectId) .regularPaymentId(regularPaymentId) + .flameImage(FlameImage.NORMAL_IMG.getImg()) .build(); } diff --git a/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java b/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java index 487726dc..3fae67ec 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/scheduler/PaymentScheduler.java @@ -45,7 +45,7 @@ public void RegularPayScheduler(){ jobLauncher.run(regularPaymentJob.regularPaymentJob(), jobParameters); } catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException | JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) { - discordFeignClient.errorMessage(discordConvertor.ErrorBatchServer("정기 결제 스케줄러", e.getMessage())); + discordFeignClient.errorMessage(discordConvertor.convertToErrorBatchServer("정기 결제 스케줄러", e.getMessage())); log.error(e.getMessage()); } @@ -66,7 +66,7 @@ public void RegularFailedPayScheduler(){ jobLauncher.run(donationFailedRetry.regularPaymentRetryJob(), jobParameters); } catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException | JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) { - discordFeignClient.errorMessage(discordConvertor.ErrorBatchServer("정기 결제 실패 재시도 스케줄러", e.getMessage())); + discordFeignClient.errorMessage(discordConvertor.convertToErrorBatchServer("정기 결제 실패 재시도 스케줄러", e.getMessage())); log.error(e.getMessage()); } } diff --git a/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java b/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java index e7b54e47..a9fb13af 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java @@ -64,7 +64,7 @@ public void regularDonationPayment() { int amount = 0; int successCnt = 0; - discordFeignClient.alertMessage(discordConvertor.AlertBatchMessage("정기 결제 스케줄러 시작", regularPayments.size())); + discordFeignClient.alertMessage(discordConvertor.convertToAlertBatchMessage("정기 결제 스케줄러 시작", regularPayments.size())); String accessToken = portOneAuthService.getTokens(); @@ -83,7 +83,7 @@ public void regularDonationPayment() { String orderId = REGULAR + createRandomOrderId(); - PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "월 달 " +"MATCH 기부금 정기 결제", userCard.getCustomerId())); + PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.convertPayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "월 달 " +"MATCH 기부금 정기 결제", userCard.getCustomerId())); String flameName = orderHelper.createFlameName(regularPayment.getUser().getName()); @@ -102,7 +102,7 @@ public void regularDonationPayment() { } donationHistoryRepository.saveAll(donationHistories); regularPaymentHistoryRepository.saveAll(requestPaymentHistories); - discordFeignClient.alertMessage(discordConvertor.AlertFinishMessage("정기 결제 스케줄러 종료", amount, regularPayments.size(),successCnt, trueCnt)); + discordFeignClient.alertMessage(discordConvertor.convertToAlertFinishMessage("정기 결제 스케줄러 종료", amount, regularPayments.size(),successCnt, trueCnt)); } } @@ -156,14 +156,14 @@ public void regularPaymentRetry() { int trueCnt = 0; int successCnt = 0 ; String accessToken = portOneAuthService.getTokens(); - discordFeignClient.alertMessage(discordConvertor.AlertBatchMessage("정기 결제 실패 한 리스트 스케줄러가 시작", requestPaymentHistories.size())); + discordFeignClient.alertMessage(discordConvertor.convertToAlertBatchMessage("정기 결제 실패 한 리스트 스케줄러가 시작", requestPaymentHistories.size())); for(RequestPaymentHistory requestPaymentHistory : requestPaymentHistories){ RegularPayment regularPayment = requestPaymentHistory.getRegularPayment(); UserCard userCard = requestPaymentHistory.getUserCard(); String orderId = REGULAR + createRandomOrderId(); - PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "월 달 " +"MATCH 기부금 정기 결제", userCard.getCustomerId())); + PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.convertPayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "월 달 " +"MATCH 기부금 정기 결제", userCard.getCustomerId())); trueCnt +=1 ; @@ -185,7 +185,7 @@ public void regularPaymentRetry() { donationUserRepository.saveAll(donationUsers); - discordFeignClient.alertMessage(discordConvertor.AlertFinishMessage("실패한 정기 결제 스케줄러가 종료", amount, requestPaymentHistories.size(),successCnt,trueCnt)); + discordFeignClient.alertMessage(discordConvertor.convertToAlertFinishMessage("실패한 정기 결제 스케줄러가 종료", amount, requestPaymentHistories.size(),successCnt,trueCnt)); } diff --git a/Match-Common/build.gradle b/Match-Common/build.gradle index 1c995b4e..250aa5c7 100644 --- a/Match-Common/build.gradle +++ b/Match-Common/build.gradle @@ -22,6 +22,9 @@ dependencies { api group: 'org.json', name: 'json', version: '20090211' + api 'org.redisson:redisson-spring-boot-starter:3.18.0' + + api 'org.springframework.boot:spring-boot-starter-mail:2.5.5' api group: 'com.sun.mail', name: 'javax.mail', version: '1.6.2' diff --git a/Match-Common/src/main/java/com/example/matchcommon/annotation/Adaptor.java b/Match-Common/src/main/java/com/example/matchcommon/annotation/Adaptor.java new file mode 100644 index 00000000..fb7d7ab8 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/annotation/Adaptor.java @@ -0,0 +1,15 @@ +package com.example.matchcommon.annotation; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Adaptor { + @AliasFor(annotation = Component.class) + String value() default ""; +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/annotation/RedissonLock.java b/Match-Common/src/main/java/com/example/matchcommon/annotation/RedissonLock.java new file mode 100644 index 00000000..4075f471 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/annotation/RedissonLock.java @@ -0,0 +1,36 @@ +package com.example.matchcommon.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RedissonLock { + + // 락 이름 + String LockName(); + /** + * 락을 걸 파라미터 키값 + */ + String key(); + + /** + * 락의 시간 단위 + */ + TimeUnit timeUnit() default TimeUnit.SECONDS; + + /** + * 락을 기다리는 시간 (default - 5s) + * 락 획득을 위해 waitTime 만큼 대기한다 + */ + long waitTime() default 10L; + + /** + * 락 임대 시간 (default - 3s) + * 락을 획득한 이후 leaseTime 이 지나면 락을 해제한다 + */ + long leaseTime() default 10L; +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/aop/AopForTransaction.java b/Match-Common/src/main/java/com/example/matchcommon/aop/AopForTransaction.java new file mode 100644 index 00000000..da9cc8c0 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/aop/AopForTransaction.java @@ -0,0 +1,15 @@ +package com.example.matchcommon.aop; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class AopForTransaction { + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { + return joinPoint.proceed(); + } +} \ No newline at end of file diff --git a/Match-Common/src/main/java/com/example/matchcommon/aop/RedissonCheckLockAop.java b/Match-Common/src/main/java/com/example/matchcommon/aop/RedissonCheckLockAop.java new file mode 100644 index 00000000..3bc20933 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/aop/RedissonCheckLockAop.java @@ -0,0 +1,70 @@ +package com.example.matchcommon.aop; + +import com.example.matchcommon.annotation.RedissonLock; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Aspect +@Component +@RequiredArgsConstructor +@Slf4j +public class RedissonCheckLockAop { + private static final String REDISSON_LOCK_PREFIX = "LOCK:"; + + private final RedissonClient redissonClient; + private final AopForTransaction aopForTransaction; + + @Around("@annotation(com.example.matchcommon.annotation.RedissonLock)") + public Object lock(final ProceedingJoinPoint joinPoint) throws Throwable { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + RedissonLock redissonLock = method.getAnnotation(RedissonLock.class); + + String key = REDISSON_LOCK_PREFIX + redissonLock.LockName() + "-" + getDynamicValue(signature.getParameterNames(), joinPoint.getArgs(), redissonLock.key()); + + RLock rLock = redissonClient.getLock("key" + key); // (1) + + try { + boolean available = rLock.tryLock(redissonLock.waitTime(), redissonLock.leaseTime(), redissonLock.timeUnit()); // (2) + if (!available) { + return false; + } + return aopForTransaction.proceed(joinPoint); // (3) + } catch (InterruptedException e) { + throw new InterruptedException(); + } finally { + try { + rLock.unlock(); // (4) + log.info("key : " + key); + log.info("unlock - redisson"); + } catch (IllegalMonitorStateException e) { + log.info("Redisson Lock Already UnLock"); + log.info("serviceName : " + method.getName()); + log.info("key : " + key); + } + } + } + + private Object getDynamicValue(String[] parameterNames, Object[] args, String key) { + ExpressionParser parser = new SpelExpressionParser(); + StandardEvaluationContext context = new StandardEvaluationContext(); + + for (int i = 0; i < parameterNames.length; i++) { + context.setVariable(parameterNames[i], args[i]); + } + + return parser.parseExpression(key).getValue(context, Object.class); + } +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java index 04b9561e..38137fb1 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/AsyncConfig.java @@ -1,5 +1,6 @@ package com.example.matchcommon.config; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurerSupport; import org.springframework.scheduling.annotation.EnableAsync; @@ -10,12 +11,35 @@ @EnableAsync public class AsyncConfig extends AsyncConfigurerSupport { - public Executor getAsyncExecutor() { + @Bean(name = "email") + public Executor emailThreadAsync() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(2); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setCorePoolSize(10); // 기본적으로 실행 대기 중인 Thread 개수 executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) - executor.setThreadNamePrefix("async-"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.setThreadNamePrefix("async-mail-thread"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.initialize(); + return executor; + } + + @Bean(name = "fcm") + public Executor fcmThreadAsync() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 + executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) + executor.setThreadNamePrefix("async-fcm-thread"); // Spring에서 생성하는 Thread 이름의 접두사 + executor.initialize(); + return executor; + } + + @Bean(name = "discord-message") + public Executor discordMessageThreadAsync() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); // 기본적으로 실행 대기 중인 Thread 개수 + executor.setMaxPoolSize(10); // 동시에 동작하는 최대 Thread 개수 + executor.setQueueCapacity(500); // CorePool이 초과될때 Queue에 저장했다가 꺼내서 실행된다. (500개까지 저장함) + executor.setThreadNamePrefix("discord-message-thread"); // Spring에서 생성하는 Thread 이름의 접두사 executor.initialize(); return executor; } diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/CacheConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/CacheConfig.java new file mode 100644 index 00000000..de1f61d3 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/config/CacheConfig.java @@ -0,0 +1,27 @@ +package com.example.matchcommon.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.ehcache.EhCacheCacheManager; +import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +@Configuration +@Slf4j +public class CacheConfig { + @Bean + public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() { + EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean(); + ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); + ehCacheManagerFactoryBean.setShared(true); + return ehCacheManagerFactoryBean; + } + + @Bean + public EhCacheCacheManager ehcacheManager(EhCacheManagerFactoryBean ehCacheManagerFactoryBean) { + EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager(); + ehCacheCacheManager.setCacheManager(ehCacheManagerFactoryBean.getObject()); + return ehCacheCacheManager; + } +} \ No newline at end of file diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/DataSourceConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/DataSourceConfig.java new file mode 100644 index 00000000..b34b493b --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/config/DataSourceConfig.java @@ -0,0 +1,62 @@ +package com.example.matchcommon.config; + +import com.zaxxer.hikari.HikariDataSource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +@Slf4j +public class DataSourceConfig { + public static final String MASTER_DATASOURCE = "masterDataSource"; + public static final String SLAVE_DATASOURCE = "slaveDataSource"; + + @Bean(MASTER_DATASOURCE) + @ConfigurationProperties(prefix = "spring.datasource.master.hikari") + public DataSource masterDataSource() { + log.info("master db active"); + return DataSourceBuilder.create() + .type(HikariDataSource.class) + .build(); + } + + @Bean(SLAVE_DATASOURCE) + @ConfigurationProperties(prefix = "spring.datasource.slave.hikari") + public DataSource slaveDataSource() { + log.info("slave db active"); + return DataSourceBuilder.create() + .type(HikariDataSource.class) + .build(); + } + + @Bean + @DependsOn({MASTER_DATASOURCE, SLAVE_DATASOURCE}) + public DataSource routingDataSource( + @Qualifier(MASTER_DATASOURCE) DataSource masterDataSource, + @Qualifier(SLAVE_DATASOURCE) DataSource slaveDataSource) { + RoutingDataSource routingDataSource = new RoutingDataSource(); + Map datasource = new HashMap<>(); + datasource.put("master", masterDataSource); + datasource.put("slave", slaveDataSource); + routingDataSource.setTargetDataSources(datasource); + routingDataSource.setDefaultTargetDataSource(masterDataSource); + return routingDataSource; + } + + @Primary + @Bean + @DependsOn("routingDataSource") + public LazyConnectionDataSourceProxy dataSource(DataSource routingDataSource){ + return new LazyConnectionDataSourceProxy(routingDataSource); + } +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/FirebaseConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/FirebaseService.java similarity index 91% rename from Match-Common/src/main/java/com/example/matchcommon/config/FirebaseConfig.java rename to Match-Common/src/main/java/com/example/matchcommon/config/FirebaseService.java index 98c1dc1a..58efd8bc 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/FirebaseConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/FirebaseService.java @@ -6,15 +6,15 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; -import java.io.FileInputStream; import java.io.IOException; import java.util.List; @Configuration @Slf4j -public class FirebaseConfig { +public class FirebaseService { @PostConstruct public void init() { @@ -31,8 +31,12 @@ public void init() { } } catch (IOException e) { log.info(e.getMessage()); + log.info("FireBase Config Failed"); // spring 뜰때 알림 서버가 잘 동작하지 않는 것이므로 바로 죽임 throw new RuntimeException(e.getMessage()); + } } + + } diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java index 18582312..cdbd4ae5 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java @@ -4,6 +4,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.SocketOptions; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -23,8 +24,11 @@ @EnableRedisRepositories( basePackages = "com.example") @RequiredArgsConstructor +@Slf4j public class RedisConfig { private final RedisProperties redisProperties; + @Value("${spring.config.activate.on-profile}") + private String profile; @Bean public RedisConnectionFactory redisConnectionFactory() { @@ -37,8 +41,14 @@ public RedisConnectionFactory redisConnectionFactory() { .build()) .commandTimeout(Duration.ofSeconds(1000L)).build(); - return new LettuceConnectionFactory(clusterConfiguration, clientConfiguration); - //return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); + if(profile.equals("prod")){ + log.info(profile + " profile"); + return new LettuceConnectionFactory(clusterConfiguration, clientConfiguration); + }else { + log.info(profile + " profile"); + return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); + } + } @Bean diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/RedissonConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/RedissonConfig.java new file mode 100644 index 00000000..fc26f113 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/config/RedissonConfig.java @@ -0,0 +1,37 @@ +package com.example.matchcommon.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class RedissonConfig { + private final RedisProperties redisProperties; + private static final String REDISSON_HOST_PREFIX = "redis://"; + + @Value("${spring.config.activate.on-profile}") + private String profile; + + @Bean + public RedissonClient redissonClient() { + Config config = new Config(); + log.info("RedissonConfig " + profile + " profile"); + if (profile.equals("prod")) { + config.useClusterServers() + .addNodeAddress(REDISSON_HOST_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort()) + // 다른 클러스터 노드도 필요에 따라 추가 + .setScanInterval(2000); + } else { + config.useSingleServer().setAddress(REDISSON_HOST_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort()); + } + return Redisson.create(config); + } +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/RoutingDataSource.java b/Match-Common/src/main/java/com/example/matchcommon/config/RoutingDataSource.java new file mode 100644 index 00000000..5b7c2527 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/config/RoutingDataSource.java @@ -0,0 +1,12 @@ +package com.example.matchcommon.config; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class RoutingDataSource extends AbstractRoutingDataSource { + + @Override + protected Object determineCurrentLookupKey() { + return (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) ? "slave" : "master"; + } +} \ No newline at end of file diff --git a/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java index e78b5afa..83dedf02 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java +++ b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java @@ -27,5 +27,10 @@ public class MatchStatic { public static final String PORT_ONE = "port_one"; + public static final String EVENT_S3_DIR = "event"; + + public static final String NOTICE_S3_DIR = "notice"; + + } diff --git a/Match-Common/src/main/java/com/example/matchcommon/constants/enums/FILTER.java b/Match-Common/src/main/java/com/example/matchcommon/constants/enums/FILTER.java new file mode 100644 index 00000000..0a9dada7 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/constants/enums/FILTER.java @@ -0,0 +1,5 @@ +package com.example.matchcommon.constants.enums; + +public enum FILTER { + RECOMMEND, LATEST +} diff --git a/Match-Common/src/main/java/com/example/matchcommon/properties/JwtProperties.java b/Match-Common/src/main/java/com/example/matchcommon/properties/JwtProperties.java index 13567d51..cbf355b2 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/properties/JwtProperties.java +++ b/Match-Common/src/main/java/com/example/matchcommon/properties/JwtProperties.java @@ -20,6 +20,4 @@ public class JwtProperties { private String refresh; private Long accessTokenSeconds; private Long refreshTokenSeconds; - - } diff --git a/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java b/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java index f0250699..ec48dc1c 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java +++ b/Match-Common/src/main/java/com/example/matchcommon/reponse/CommonResponse.java @@ -22,7 +22,7 @@ public class CommonResponse { private final String message; @Schema(description = "응답 코드", required = true, example = "1000") private final String code; - @Schema(description = "응답 결과", required = false, example = "응답 결과") + @Schema(description = "응답 결과", required = false) @JsonInclude(JsonInclude.Include.NON_NULL) private T result; diff --git a/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java b/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java index 44d4763c..a6afda85 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java +++ b/Match-Common/src/main/java/com/example/matchcommon/reponse/PageResponse.java @@ -1,17 +1,20 @@ package com.example.matchcommon.reponse; import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.AllArgsConstructor; import lombok.Getter; +import java.io.Serializable; + @Getter @AllArgsConstructor @Schema(description = "페이징 처리 응답") -public class PageResponse { +public class PageResponse implements Serializable { @Schema(description = "마지막 페이지 유무", required = true, example = "true") private final Boolean isLast; @Schema(description = "총 요소 갯수", required = true, example = "10") private final long totalCnt; - @Schema(description = "요소", required = true, example = "true") + @Schema(description = "요소", required = true) private final T contents; } diff --git a/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java b/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java index 3735c637..c8586cdb 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java +++ b/Match-Common/src/main/java/com/example/matchcommon/service/MailService.java @@ -53,7 +53,7 @@ private SimpleMailMessage createEmailForm(String toEmail, return message; } - @Async + @Async("email") public void sendEmailMessage(String email, String code){ try { MimeMessage message = emailSender.createMimeMessage(); diff --git a/Match-Common/src/main/resources/application-common.yml b/Match-Common/src/main/resources/application-common.yml index ece22239..faa47706 100644 --- a/Match-Common/src/main/resources/application-common.yml +++ b/Match-Common/src/main/resources/application-common.yml @@ -81,3 +81,4 @@ portone: key: ${PORTONE_KEY} secret: ${PORTONE_SECRET} billmid: ${PORTONE_BILL_MID} + diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java index e794bd4f..42e74dc6 100644 --- a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java +++ b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationHistory.java @@ -24,8 +24,12 @@ public class QDonationHistory extends EntityPathBase { public final com.example.matchdomain.common.model.QBaseEntity _super = new com.example.matchdomain.common.model.QBaseEntity(this); + public final ListPath> changeIdLists = this.>createList("changeIdLists", Long.class, NumberPath.class, PathInits.DIRECT2); + public final NumberPath cnt = createNumber("cnt", Integer.class); + public final ListPath> completeIdLists = this.>createList("completeIdLists", Long.class, NumberPath.class, PathInits.DIRECT2); + //inherited public final DateTimePath createdAt = _super.createdAt; @@ -33,8 +37,6 @@ public class QDonationHistory extends EntityPathBase { public final NumberPath donationUserId = createNumber("donationUserId", Long.class); - public final StringPath flameImage = createString("flameImage"); - public final ListPath historyImages = this.createList("historyImages", HistoryImage.class, QHistoryImage.class, PathInits.DIRECT2); public final EnumPath historyStatus = createEnum("historyStatus", com.example.matchdomain.donation.entity.enums.HistoryStatus.class); diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java index e46715d1..4a2d38f9 100644 --- a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java +++ b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QDonationUser.java @@ -31,6 +31,10 @@ public class QDonationUser extends EntityPathBase { public final EnumPath donationStatus = createEnum("donationStatus", com.example.matchdomain.donation.entity.enums.DonationStatus.class); + public final StringPath flameImage = createString("flameImage"); + + public final EnumPath flameType = createEnum("flameType", com.example.matchdomain.donation.entity.flameEnum.FlameType.class); + public final NumberPath id = createNumber("id", Long.class); public final StringPath inherenceName = createString("inherenceName"); diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/user/entity/QUser.java b/Match-Domain/src/main/generated/com/example/matchdomain/user/entity/QUser.java index 804c5cb4..db30ac52 100644 --- a/Match-Domain/src/main/generated/com/example/matchdomain/user/entity/QUser.java +++ b/Match-Domain/src/main/generated/com/example/matchdomain/user/entity/QUser.java @@ -67,6 +67,8 @@ public class QUser extends EntityPathBase { public final ListPath userCard = this.createList("userCard", com.example.matchdomain.donation.entity.UserCard.class, com.example.matchdomain.donation.entity.QUserCard.class, PathInits.DIRECT2); + public final ListPath userFcmTokens = this.createList("userFcmTokens", UserFcmToken.class, QUserFcmToken.class, PathInits.DIRECT2); + public final StringPath username = createString("username"); public QUser(String variable) { diff --git a/Match-Domain/src/main/java/com/example/matchdomain/banner/adaptor/BannerAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/banner/adaptor/BannerAdaptor.java new file mode 100644 index 00000000..2fe52148 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/banner/adaptor/BannerAdaptor.java @@ -0,0 +1,21 @@ +package com.example.matchdomain.banner.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchdomain.banner.entity.Banner; +import com.example.matchdomain.banner.repository.BannerRepository; +import com.example.matchdomain.keyword.entity.SearchKeyword; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; + +import java.util.ArrayList; +import java.util.List; + +@Adaptor +@RequiredArgsConstructor +public class BannerAdaptor { + private final BannerRepository bannerRepository; + + public List getBannerList() { + return bannerRepository.findAllByOrderByCreatedAtDesc(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/banner/entity/Banner.java b/Match-Domain/src/main/java/com/example/matchdomain/banner/entity/Banner.java new file mode 100644 index 00000000..c48067e9 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/banner/entity/Banner.java @@ -0,0 +1,42 @@ +package com.example.matchdomain.banner.entity; + +import com.example.matchdomain.banner.enums.BannerType; +import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.event.entity.Event; +import lombok.*; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.*; + +@Entity +@Table(name = "`Banner`") +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@BatchSize(size = 100) +@DynamicInsert +public class Banner extends BaseEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + private BannerType bannerType; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "eventId",nullable = false, insertable=false, updatable=false) + private Event event; + + @Column(name="eventId") + private Long eventId; + + private String bannerImg; + + private String contentsUrl; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/banner/enums/BannerType.java b/Match-Domain/src/main/java/com/example/matchdomain/banner/enums/BannerType.java new file mode 100644 index 00000000..fd815459 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/banner/enums/BannerType.java @@ -0,0 +1,14 @@ +package com.example.matchdomain.banner.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum BannerType { + EVENT("EVENT", "이벤트"), + CONTENTS("CONTENTS", "SNS 콘텐츠"); + private final String value; + + private final String type; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/banner/repository/BannerRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/banner/repository/BannerRepository.java new file mode 100644 index 00000000..35c2ce85 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/banner/repository/BannerRepository.java @@ -0,0 +1,10 @@ +package com.example.matchdomain.banner.repository; + +import com.example.matchdomain.banner.entity.Banner; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface BannerRepository extends JpaRepository { + List findAllByOrderByCreatedAtDesc(); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/common/model/ContentsEntity.java b/Match-Domain/src/main/java/com/example/matchdomain/common/model/ContentsEntity.java new file mode 100644 index 00000000..8bbc687e --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/common/model/ContentsEntity.java @@ -0,0 +1,37 @@ +package com.example.matchdomain.common.model; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@NoArgsConstructor +@SuperBuilder +@EntityListeners(AuditingEntityListener.class) +public abstract class ContentsEntity { + @Enumerated(EnumType.STRING) + private ContentsType contentsType; + + private String contents; + + + @Column(name="createdAt",updatable = false) + @CreatedDate + private LocalDateTime createdAt; + + @Column(name="updatedAt") + @LastModifiedDate + private LocalDateTime updatedAt; + + @Enumerated(EnumType.STRING) + private Status status = Status.ACTIVE; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/common/model/ContentsType.java b/Match-Domain/src/main/java/com/example/matchdomain/common/model/ContentsType.java new file mode 100644 index 00000000..1dcaee18 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/common/model/ContentsType.java @@ -0,0 +1,16 @@ +package com.example.matchdomain.common.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum ContentsType { + IMG("IMG", "이미지"), + TEXT("TEXT", "텍스트"); + + private final String value; + + private final String type; + +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/common/model/MessageType.java b/Match-Domain/src/main/java/com/example/matchdomain/common/model/MessageType.java new file mode 100644 index 00000000..c103d8ec --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/common/model/MessageType.java @@ -0,0 +1,15 @@ +package com.example.matchdomain.common.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum MessageType { + PAY_SUCCESS("결제 완료 후"), + PAY_ONE_DAY("결제 완료 하루 이후"), + UNDER("집행 중일 때"), + COMPLETE("전달 완료일 때"); + + private final String description; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/common/model/RandomMessage.java b/Match-Domain/src/main/java/com/example/matchdomain/common/model/RandomMessage.java new file mode 100644 index 00000000..856a8ef1 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/common/model/RandomMessage.java @@ -0,0 +1,27 @@ +package com.example.matchdomain.common.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import static com.example.matchdomain.common.model.MessageType.*; + +@Getter +@AllArgsConstructor +public enum RandomMessage { + WARM(PAY_SUCCESS, "도움이 필요한 곳에 누구보다 따뜻한 온기를 전할게요."), + THANK_YOU(PAY_SUCCESS, "태어나게 해줘서 감사해요! 식사 맛있게 드세요 :)"), + WARM_MIND(PAY_SUCCESS,"기부자님의 따뜻한 마음으로 제가 태어났어요."), + + WAITING(PAY_ONE_DAY, "저는 지금 친구들을 기다리고 있어요"), + WAITING2(PAY_ONE_DAY, "잠시만 기다려주세요! 불꽃들이 당신을 위해 준비 중입니다. 🔥"), + WAITING3(PAY_ONE_DAY, "불꽃이 곧 타오르게 됩니다. 기대해주세요! ✨"), + WAITING4(PAY_ONE_DAY, "잠시만요! 더 멋진 불꽃을 위해 마지막 터치를 하고 있어요. 💥"), + WAITING5(PAY_ONE_DAY, "당신을 기다리고 있는 불꽃이 더 빛나게 하기 위해 준비 중입니다! 🌟"), + + MOVING(UNDER,"저는 지금 다른 불꽃이 친구들과 함께 후원처로 이동하고있어요"), + COMPLETED(COMPLETE,"저는 지금 후원처에 전달되었어요"); + + private final MessageType messageType; + private final String message; +} + diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationAdaptor.java new file mode 100644 index 00000000..e72d6d03 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationAdaptor.java @@ -0,0 +1,89 @@ +package com.example.matchdomain.donation.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.NotFoundException; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.enums.DonationStatus; +import com.example.matchdomain.donation.repository.DonationUserRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +import static com.example.matchdomain.common.model.Status.ACTIVE; +import static com.example.matchdomain.donation.entity.enums.DonationStatus.EXECUTION_REFUND; +import static com.example.matchdomain.donation.exception.DonationListErrorCode.FILTER_NOT_EXIST; +import static com.example.matchdomain.donation.exception.DonationRefundErrorCode.DONATION_NOT_EXIST; +import static com.example.matchdomain.project.entity.enums.ImageRepresentStatus.REPRESENT; + +@Adaptor +@RequiredArgsConstructor +public class DonationAdaptor { + private final DonationUserRepository donationUserRepository; + + public Page findDonationList(Long userId, int filter, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + Page donationUsers = null; + if(filter == 0){ + donationUsers = donationUserRepository.findByUserIdAndStatusAndDonationStatusNotOrderByCreatedAtDesc(userId, ACTIVE,EXECUTION_REFUND, pageable); + } + else if(filter == 1){ + donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(userId, DonationStatus.EXECUTION_SUCCESS, ACTIVE, pageable); + } + else if(filter == 2){ + donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(userId,DonationStatus.EXECUTION_SUCCESS, ACTIVE, pageable); + + }else if(filter == 3){ + donationUsers = donationUserRepository.findByUserIdAndDonationStatusAndStatusOrderByCreatedAtDesc(userId, DonationStatus.EXECUTION_SUCCESS, ACTIVE, pageable); + + }else{ + throw new BadRequestException(FILTER_NOT_EXIST); + } + + return donationUsers; + } + + public DonationUser findById(Long donationId){ + return donationUserRepository.findById(donationId).orElseThrow(() -> new NotFoundException(DONATION_NOT_EXIST)); + } + + public List findByDonationCount(User user){ + return donationUserRepository.findByUserAndDonationStatusNotAndStatus(user, EXECUTION_REFUND, ACTIVE); + } + + public Page findFlameList(User user, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + + return donationUserRepository.getFlameList(user.getId(), pageable); + } + + public List findPayList(Long regularPayId){ + return donationUserRepository.findByRegularPaymentIdAndStatusOrderByCreatedAtDesc(regularPayId, ACTIVE); + } + + public Page getFlameProjectList(User user,String content, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + + return donationUserRepository.findByUserAndInherenceNameContainingAndProject_ProjectImg_RepresentStatusOrderByCreatedAtDesc(user, content, REPRESENT, pageable); + } + + public Page findDonationUsers(Long projectId, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + return donationUserRepository.findByProjectId(projectId, pageable); + } + + public Page findByUser(User user, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + return donationUserRepository.findByUserAndDonationStatusNotOrderByCreatedAtDesc(user, EXECUTION_REFUND, pageable); + } + + public List findDonationListsByUser(User user) { + return donationUserRepository.findByUser(user); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationHistoryAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationHistoryAdaptor.java new file mode 100644 index 00000000..ab5db77a --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/DonationHistoryAdaptor.java @@ -0,0 +1,38 @@ +package com.example.matchdomain.donation.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchdomain.donation.entity.DonationHistory; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.enums.HistoryStatus; +import com.example.matchdomain.donation.repository.DonationHistoryRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import static com.example.matchdomain.donation.entity.enums.HistoryStatus.CREATE; + +@Adaptor +@RequiredArgsConstructor +public class DonationHistoryAdaptor { + private final DonationHistoryRepository donationHistoryRepository; + + public Page findDonationRegularList(Long regularPayId, Long projectId, HistoryStatus turnOn, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + return donationHistoryRepository.findByDonationUser_RegularPaymentIdOrRegularPaymentIdOrProjectIdAndHistoryStatusNotOrderByCreatedAtAsc(regularPayId, regularPayId, projectId, HistoryStatus.TURN_ON ,pageable); + } + + public Page findMatchHistory(Long projectId, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + return donationHistoryRepository.findByDonationUser_ProjectOrProjectIdIdOrderByCreatedAtDesc(projectId, pageable); + } + + public Page findDonationHistory(DonationUser donationUser, Long donationId, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + return donationHistoryRepository.getDonationHistoryCustom(donationUser.getRegularPaymentId(), donationId, CREATE, pageable, donationUser.getProjectId()); + } + + public void saveDonationHistory(DonationHistory donationHistory) { + donationHistoryRepository.save(donationHistory); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RegularPaymentAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RegularPaymentAdaptor.java new file mode 100644 index 00000000..ab5829ea --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/adaptor/RegularPaymentAdaptor.java @@ -0,0 +1,58 @@ +package com.example.matchdomain.donation.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.donation.entity.RegularPayment; +import com.example.matchdomain.donation.repository.RegularPaymentRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.util.List; +import java.util.stream.Collectors; + +import static com.example.matchdomain.donation.entity.enums.RegularPayStatus.PROCEEDING; +import static com.example.matchdomain.donation.exception.CancelRegularPayErrorCode.REGULAR_PAY_NOT_EXIST; +import static com.example.matchdomain.donation.exception.GetRegularErrorCode.REGULAR_NOT_EXIST; + +@Adaptor +@RequiredArgsConstructor +public class RegularPaymentAdaptor { + private final RegularPaymentRepository regularPaymentRepository; + + public RegularPayment findRegularPaymentByStatus(Long regularId, Status status) { + return regularPaymentRepository.findByIdAndStatus(regularId, status).orElseThrow(() -> new BadRequestException(REGULAR_PAY_NOT_EXIST)); + } + + public RegularPayment findById(Long regularPayId){ + return regularPaymentRepository.findById(regularPayId).orElseThrow(()-> new BadRequestException(REGULAR_NOT_EXIST)); + } + public Page findByUser(User user, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + return regularPaymentRepository.findByUserOrderByCreatedAtDesc(user, pageable); + } + + public Page findBurningFlameList(User user, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + return regularPaymentRepository.findRegularListCustom(user, PROCEEDING, pageable); + } + + public Page getBurningFlameList(User user, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + return regularPaymentRepository.getBurningFlameListCustom(user.getId(), PROCEEDING.getValue(), pageable); + } + + public List findByIdList(Long regularPayId, User user){ + return regularPaymentRepository.findByIdAndUserOrderByCreatedAtDesc(regularPayId, user).stream() + .map(RegularPayment :: getId) + .collect(Collectors.toList()); + } + + public List findByCardId(Long cardId) { + return regularPaymentRepository.findByUserCardIdAndRegularPayStatus(cardId, PROCEEDING); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java index 6f191716..fbd6380b 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationHistory.java @@ -47,8 +47,6 @@ public class DonationHistory extends BaseEntity { @Column(name="donationUserId") private Long donationUserId; - private String flameImage; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "projectId",nullable = false, insertable=false, updatable=false) private Project project; @@ -60,4 +58,13 @@ public class DonationHistory extends BaseEntity { @JoinColumn(name = "donationHistoryId") private List historyImages = new ArrayList<>(); + @Column + @ElementCollection + private List completeIdLists; + + @Column + @ElementCollection + private List changeIdLists; + + } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java index 4d9ad468..96194a7b 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/DonationUser.java @@ -4,6 +4,7 @@ import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.enums.PayMethod; import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.donation.entity.flameEnum.FlameType; import com.example.matchdomain.project.entity.Project; import com.example.matchdomain.user.entity.User; import lombok.*; @@ -71,6 +72,11 @@ public class DonationUser extends BaseEntity { @Enumerated(EnumType.STRING) private RegularStatus regularStatus; + private String flameImage; + + @Enumerated(EnumType.STRING) + private FlameType flameType = FlameType.NORMAL_FLAME; + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "donationUserId") @BatchSize(size = 100) diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/RegularStatus.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/RegularStatus.java index f4ddc176..32ad4d3a 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/RegularStatus.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/enums/RegularStatus.java @@ -10,7 +10,7 @@ @AllArgsConstructor public enum RegularStatus { REGULAR("REGULAR","정기 후원"), - ONE_TIME("ONE_TIME","일회성 후원"); + ONE_TIME("ONE_TIME","일시 후원"); private final String value; private final String name; diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/FlameImage.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/FlameImage.java new file mode 100644 index 00000000..9654a99e --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/FlameImage.java @@ -0,0 +1,11 @@ +package com.example.matchdomain.donation.entity.flameEnum; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum FlameImage { + NORMAL_IMG("https://match-image.s3.ap-northeast-2.amazonaws.com/flame.png"); + private final String img; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/FlameType.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/FlameType.java new file mode 100644 index 00000000..81f9c2ba --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/flameEnum/FlameType.java @@ -0,0 +1,14 @@ +package com.example.matchdomain.donation.entity.flameEnum; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum FlameType { + NORMAL_FLAME("NORMAL_FLAME", "일반 불꽃"); + + private final String value; + + private final String type; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/exception/CancelRegularPayErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/exception/CancelRegularPayErrorCode.java index b3a3d0ef..64e0718f 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/exception/CancelRegularPayErrorCode.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/exception/CancelRegularPayErrorCode.java @@ -17,9 +17,11 @@ @AllArgsConstructor public enum CancelRegularPayErrorCode implements BaseErrorCode { @ExplainError("해당 도네이션이 존재하지 않습니다.") - REGULAR_PAY_NOT_EXIST(NOT_FOUND,"REGULAR001", "해당 정기기부 내역이 존재하지 않습니다."), + REGULAR_PAY_NOT_EXIST(NOT_FOUND,"REGULAR_CANCEL_001", "해당 정기기부 내역이 존재하지 않습니다."), @ExplainError("도네이션 환불 권한이 없습니다.") - REGULAR_PAY_NOT_CORRECT_USER(BAD_REQUEST,"REGULAR002","정기기부를 취소할 권한이 없습니다. 삭제권한이 없습니다."); + REGULAR_PAY_NOT_CORRECT_USER(BAD_REQUEST,"REGULAR_CANCEL_002","정기기부를 취소할 권한이 없습니다. 삭제권한이 없습니다."), + @ExplainError("현재 기부 상태가 진행중이 아니므로 취소할 수 없음") + REGULAR_PAY_NOT_STATUS(BAD_REQUEST,"REGULAR_CANCEL_003", "이미 정기기부를 취소한 상태입니다."); private final HttpStatus httpStatus; diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java index a761f21a..d35adaeb 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationCustomRepositoryImpl.java @@ -11,6 +11,8 @@ import java.util.List; +import static com.example.matchdomain.donation.entity.enums.HistoryStatus.COMPLETE; + @RequiredArgsConstructor public class DonationCustomRepositoryImpl implements DonationCustomRepository{ private final JPAQueryFactory queryFactory; @@ -30,6 +32,15 @@ public Page getDonationHistoryCustom(Long regularPaymentId, Lon .and(donationHistory.historyStatus.ne(HistoryStatus.TURN_ON)) .and(donationHistory.historyStatus.ne(HistoryStatus.START)) .and(donationHistory.historyStatus.ne(HistoryStatus.FINISH)) + .and( + donationHistory.historyStatus.eq(HistoryStatus.COMPLETE) + .and(donationHistory.completeIdLists.contains(donationId)) + .or(donationHistory.historyStatus.ne(HistoryStatus.COMPLETE))) + .and( + donationHistory.historyStatus.eq(HistoryStatus.CHANGE) + .and(donationHistory.changeIdLists.contains(donationId)) + .or(donationHistory.historyStatus.ne(HistoryStatus.CHANGE))) + ) .orderBy(donationHistory.createdAt.asc()) .offset(pageable.getOffset()) @@ -44,6 +55,15 @@ public Page getDonationHistoryCustom(Long regularPaymentId, Lon .and(donationHistory.historyStatus.ne(HistoryStatus.TURN_ON)) .and(donationHistory.historyStatus.ne(HistoryStatus.START)) .and(donationHistory.historyStatus.ne(HistoryStatus.FINISH)) + .and( + donationHistory.historyStatus.eq(HistoryStatus.COMPLETE) + .and(donationHistory.completeIdLists.contains(donationId)) + .or(donationHistory.historyStatus.ne(HistoryStatus.COMPLETE))) + .and( + donationHistory.historyStatus.eq(HistoryStatus.CHANGE) + .and(donationHistory.changeIdLists.contains(donationId)) + .or(donationHistory.historyStatus.ne(HistoryStatus.CHANGE))) + ) .orderBy(donationHistory.createdAt.asc()); return PageableExecutionUtils.getPage(donationHistories, pageable, countQuery::fetchCount); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationHistoryRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationHistoryRepository.java index 2fe78da4..5aac4716 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationHistoryRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationHistoryRepository.java @@ -27,10 +27,10 @@ public interface DonationHistoryRepository extends JpaRepository findByDonationUser_ProjectOrProjectIdIdOrderByCreatedAtAsc(@Param("projectId") Long projectId, Pageable pageable); + Page findByDonationUser_ProjectOrProjectIdIdOrderByCreatedAtDesc(@Param("projectId") Long projectId, Pageable pageable); } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java index dd20e282..727f02f5 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/DonationUserRepository.java @@ -110,6 +110,13 @@ public interface DonationUserRepository extends JpaRepository List findByIdIn(List donationUserLists); + @Query(value = "select du from DonationUser du join fetch du.project p " + + " where du.user = :user and du.donationStatus != :donationStatus order by du.createdAt asc", + countQuery = "select count(du) from DonationUser du where du.user = :user and du.donationStatus != :donationStatus") + Page findByUserAndDonationStatusNotOrderByCreatedAtDesc(@Param("user") User user, @Param("donationStatus") DonationStatus donationStatus, Pageable pageable); + + List findByUser(User user); + interface flameList { Long getRegularPayId(); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentCustomRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentCustomRepository.java new file mode 100644 index 00000000..7330b3e4 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentCustomRepository.java @@ -0,0 +1,11 @@ +package com.example.matchdomain.donation.repository; + +import com.example.matchdomain.donation.entity.RegularPayment; +import com.example.matchdomain.donation.entity.enums.RegularPayStatus; +import com.example.matchdomain.user.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface RegularPaymentCustomRepository { + Page findRegularListCustom(User user, RegularPayStatus regularPayStatus, Pageable pageable); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentCustomRepositoryImpl.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentCustomRepositoryImpl.java new file mode 100644 index 00000000..6f4e42ba --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentCustomRepositoryImpl.java @@ -0,0 +1,49 @@ +package com.example.matchdomain.donation.repository; + +import com.example.matchdomain.donation.entity.QDonationUser; +import com.example.matchdomain.donation.entity.QRegularPayment; +import com.example.matchdomain.donation.entity.RegularPayment; +import com.example.matchdomain.donation.entity.enums.RegularPayStatus; +import com.example.matchdomain.user.entity.User; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.support.PageableExecutionUtils; + +import java.util.List; + +@RequiredArgsConstructor +public class RegularPaymentCustomRepositoryImpl implements RegularPaymentCustomRepository{ + private final JPAQueryFactory queryFactory; + @Override + public Page findRegularListCustom(User user, RegularPayStatus regularPayStatus, Pageable pageable) { + QRegularPayment regularPayment = QRegularPayment.regularPayment; + QDonationUser donationUser = QDonationUser.donationUser; + + List regularPayments = + queryFactory.selectFrom(regularPayment) + .leftJoin(donationUser).on(regularPayment.id.eq(donationUser.regularPaymentId)) + .where(donationUser.createdAt.eq( + JPAExpressions + .select(donationUser.createdAt.max()) + .from(donationUser) + .where(donationUser.regularPayment.eq(regularPayment))) + .and(regularPayment.user.eq(user)) + .and(regularPayment.regularPayStatus.eq(regularPayStatus)) + + ) + .orderBy(regularPayment.createdAt.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + JPAQuery countQuery = queryFactory + .selectFrom(regularPayment) + .where(regularPayment.user.eq(user) + .and(regularPayment.regularPayStatus.eq(regularPayStatus))); + + return PageableExecutionUtils.getPage(regularPayments, pageable, countQuery::fetchCount); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java index 17f8f576..5f42b8d0 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/repository/RegularPaymentRepository.java @@ -2,10 +2,12 @@ import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.DonationUser; import com.example.matchdomain.donation.entity.enums.RegularPayStatus; import com.example.matchdomain.donation.entity.RegularPayment; import com.example.matchdomain.user.entity.User; -import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -14,22 +16,44 @@ import java.util.Optional; -public interface RegularPaymentRepository extends JpaRepository { +public interface RegularPaymentRepository extends JpaRepository, RegularPaymentCustomRepository { Optional findByIdAndStatus(Long regularId, Status status); - @EntityGraph(attributePaths = "userCard") - List findByPayDateGreaterThanEqualAndStatus(int currentDay, Status status); - - @EntityGraph(attributePaths = "userCard") - List findByPayDateAndStatus(int currentDay, Status status); - List findByPayDateGreaterThanEqualAndStatusAndRegularPayStatus(int currentDay, Status status, RegularPayStatus regularPayStatus); @Query("SELECT RP FROM RegularPayment RP join fetch RP.user where RP.projectId=:id and RP.regularPayStatus=:regularPayStatus") List findByProjectIdAndRegularPayStatus(@Param("id") Long id,@Param("regularPayStatus") RegularPayStatus regularPayStatus); List findByUser(User user); - + List findByUserCardId(Long cardId); + + Page findByUserOrderByCreatedAtDesc(User user, Pageable pageable); + + @Query(value = "select RP.id as 'regularPayId', P.usages as 'usages', DU.inherenceName as 'inherenceName', DU.flameImage as 'image'" + + "from RegularPayment RP " + + "join Project P on P.id = RP.projectId " + + "join DonationUser DU on DU.regularPaymentId = RP.id " + + "where RP.userId = :userId and regularPayStatus = :regularPayStatus and DU.createdAt = " + + "(select max(DU2.createdAt) from DonationUser DU2 where DU2.regularPaymentId = RP.id )", + nativeQuery = true, + countQuery = "select count(*) from RegularPayment RP where RP.userId = :userId and regularPayStatus = :regularPayStatus") + Page getBurningFlameListCustom(@Param("userId") Long userId, @Param("regularPayStatus") String regularPayStatus, + Pageable pageable); + + List findByIdAndUserOrderByCreatedAtDesc(Long regularPayId, User user); + + List findByUserCardIdAndRegularPayStatus(Long cardId, RegularPayStatus regularPayStatus); + + interface RegularPaymentFlame { + Long getRegularPayId(); + + String getUsages(); + + String getInherenceName(); + + String getImage(); + + } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/adaptor/EventAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/event/adaptor/EventAdaptor.java new file mode 100644 index 00000000..42b54cff --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/adaptor/EventAdaptor.java @@ -0,0 +1,27 @@ +package com.example.matchdomain.event.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchdomain.event.entity.Event; +import com.example.matchdomain.event.repository.EventRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import static com.example.matchdomain.event.exception.GetEventErrorCode.NOT_EXIST_EVENT; + +@Adaptor +@RequiredArgsConstructor +public class EventAdaptor { + private final EventRepository eventRepository; + + public Page findEvent(int page, int size) { + Pageable pageable = PageRequest.of(page, size); + return eventRepository.findAllByOrderByCreatedAtDesc(pageable); + } + + public Event findByEvent(Long eventId) { + return eventRepository.findById(eventId).orElseThrow(() -> new BadRequestException(NOT_EXIST_EVENT)); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/entity/Event.java b/Match-Domain/src/main/java/com/example/matchdomain/event/entity/Event.java new file mode 100644 index 00000000..b75cb555 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/entity/Event.java @@ -0,0 +1,51 @@ +package com.example.matchdomain.event.entity; + +import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.event.enums.EventType; +import lombok.*; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "Event") +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@BatchSize(size = 20) +@DynamicInsert +public class Event extends BaseEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "eventId") + @BatchSize(size = 20) + private List eventContents = new ArrayList<>(); + + @Enumerated(EnumType.STRING) + private EventType eventType; + + private String title; + + private String smallTitle; + + private String thumbnail; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate eventStartDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate eventEndDate; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/entity/EventContent.java b/Match-Domain/src/main/java/com/example/matchdomain/event/entity/EventContent.java new file mode 100644 index 00000000..9cefa4e8 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/entity/EventContent.java @@ -0,0 +1,36 @@ +package com.example.matchdomain.event.entity; + +import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.common.model.ContentsEntity; +import com.example.matchdomain.common.model.ContentsType; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.*; + +@Entity +@Table(name = "EventContent") +@Getter +@Setter +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@BatchSize(size = 100) +@DynamicInsert +public class EventContent extends ContentsEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "eventId", nullable = false, insertable = false, updatable = false) + private Event event; + + @Column(name = "eventId") + private Long eventId; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/enums/EventStatus.java b/Match-Domain/src/main/java/com/example/matchdomain/event/enums/EventStatus.java new file mode 100644 index 00000000..36d9920a --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/enums/EventStatus.java @@ -0,0 +1,13 @@ +package com.example.matchdomain.event.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum EventStatus { + UNDER("진행중"), + FINISH("종료"); + + private final String value; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/enums/EventType.java b/Match-Domain/src/main/java/com/example/matchdomain/event/enums/EventType.java new file mode 100644 index 00000000..a73c22be --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/enums/EventType.java @@ -0,0 +1,14 @@ +package com.example.matchdomain.event.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum EventType { + MATCHING("MATCHING", "매칭 기부"), + MATCH_EVENT("MATCH_EVENT", "매치 이벤트"); + private final String value; + + private final String type; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/exception/GetEventErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/event/exception/GetEventErrorCode.java new file mode 100644 index 00000000..8df18e23 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/exception/GetEventErrorCode.java @@ -0,0 +1,40 @@ +package com.example.matchdomain.event.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.Objects; + +@Getter +@AllArgsConstructor +public enum GetEventErrorCode implements BaseErrorCode { + + @ExplainError("해당 이벤트는 존재하지 않습니다.") + NOT_EXIST_EVENT(HttpStatus.BAD_REQUEST,false,"EVENT_001","해당 이벤트는 존재하지 않습니다.."); + + private final HttpStatus httpStatus; + private final boolean isSuccess; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.builder().message(message).code(code).isSuccess(false).build(); + } + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/repository/EventContentRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/event/repository/EventContentRepository.java new file mode 100644 index 00000000..eeab6374 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/repository/EventContentRepository.java @@ -0,0 +1,7 @@ +package com.example.matchdomain.event.repository; + +import com.example.matchdomain.event.entity.EventContent; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface EventContentRepository extends JpaRepository { +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/event/repository/EventRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/event/repository/EventRepository.java new file mode 100644 index 00000000..63afc174 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/event/repository/EventRepository.java @@ -0,0 +1,10 @@ +package com.example.matchdomain.event.repository; + +import com.example.matchdomain.event.entity.Event; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface EventRepository extends JpaRepository { + Page findAllByOrderByCreatedAtDesc(Pageable pageable); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/keyword/entity/SearchKeyword.java b/Match-Domain/src/main/java/com/example/matchdomain/keyword/entity/SearchKeyword.java new file mode 100644 index 00000000..e62a776f --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/keyword/entity/SearchKeyword.java @@ -0,0 +1,30 @@ +package com.example.matchdomain.keyword.entity; + +import com.example.matchdomain.common.model.BaseEntity; +import lombok.*; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.*; + +@Entity +@Table(name = "SearchKeyword") +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@DynamicInsert +@BatchSize(size = 20) +public class SearchKeyword extends BaseEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private int priority; + + private String keyword; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/keyword/repository/SearchKeywordRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/keyword/repository/SearchKeywordRepository.java new file mode 100644 index 00000000..8c6cb89d --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/keyword/repository/SearchKeywordRepository.java @@ -0,0 +1,10 @@ +package com.example.matchdomain.keyword.repository; + +import com.example.matchdomain.keyword.entity.SearchKeyword; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface SearchKeywordRepository extends JpaRepository { + List findAllByOrderByPriorityAsc(); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notice/adaptor/NoticeAdapter.java b/Match-Domain/src/main/java/com/example/matchdomain/notice/adaptor/NoticeAdapter.java new file mode 100644 index 00000000..c43b61c6 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notice/adaptor/NoticeAdapter.java @@ -0,0 +1,27 @@ +package com.example.matchdomain.notice.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchdomain.notice.entity.Notice; +import com.example.matchdomain.notice.repository.NoticeRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import static com.example.matchdomain.notice.exception.GetNoticeErrorCode.NOT_EXIST_NOTICE; + +@Adaptor +@RequiredArgsConstructor +public class NoticeAdapter { + private final NoticeRepository noticeRepository; + + public Page getNoticeList(int page, int size) { + Pageable pageable = PageRequest.of(page, size); + return noticeRepository.findAllByOrderByCreatedAtDesc(pageable); + } + + public Notice findNoticeDetail(Long noticeId) { + return noticeRepository.findById(noticeId).orElseThrow(() -> new BadRequestException(NOT_EXIST_NOTICE)); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notice/entity/Notice.java b/Match-Domain/src/main/java/com/example/matchdomain/notice/entity/Notice.java new file mode 100644 index 00000000..93e69d0e --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notice/entity/Notice.java @@ -0,0 +1,40 @@ +package com.example.matchdomain.notice.entity; + +import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.notice.enums.NoticeType; +import lombok.*; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "Notice") +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@BatchSize(size = 10) +@DynamicInsert +public class Notice extends BaseEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "noticeId") + @BatchSize(size = 20) + private List noticeContents = new ArrayList<>(); + + @Enumerated(EnumType.STRING) + private NoticeType noticeType; + + private String title; + +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notice/entity/NoticeContent.java b/Match-Domain/src/main/java/com/example/matchdomain/notice/entity/NoticeContent.java new file mode 100644 index 00000000..d0161a17 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notice/entity/NoticeContent.java @@ -0,0 +1,37 @@ +package com.example.matchdomain.notice.entity; + +import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.common.model.ContentsEntity; +import com.example.matchdomain.common.model.ContentsType; +import com.example.matchdomain.event.entity.Event; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.*; + +@Entity +@Table(name = "NoticeContent") +@Getter +@Setter +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@BatchSize(size = 10) +@DynamicInsert +public class NoticeContent extends ContentsEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "noticeId",nullable = false, insertable=false, updatable=false) + private Notice notice; + + @Column(name="noticeId") + private Long noticeId; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notice/enums/NoticeType.java b/Match-Domain/src/main/java/com/example/matchdomain/notice/enums/NoticeType.java new file mode 100644 index 00000000..a19759eb --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notice/enums/NoticeType.java @@ -0,0 +1,24 @@ +package com.example.matchdomain.notice.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum NoticeType { + EVENT("EVENT", "이벤트 및 행사 공지"), + SCHEDULE("SCHEDULE", "일정 및 휴무 공지"), + SERVICE_UPDATE("SERVICE_UPDATE", "서비스 업데이트"), + JOB("JOB", "채용 및 구인 정보"), + SECURITY("SECURITY", "보안 공지"), + IMPORTANT("IMPORTANT", "중요 공지"), + EMERGENCY("EMERGENCY", "주요 이슈 및 재난 공지"), + ADMIN("ADMIN", "행정 및 관리 공지"), + EDUCATION("EDUCATION", "학교 및 교육 공지"), + HEALTH("HEALTH", "건강 및 의료 공지"), + PROJECT("PROJECT", "주요 프로젝트 및 연구 공지"), + SOCIAL("SOCIAL", "사회적 활동 공지"); + + private final String value; + private final String type; +} \ No newline at end of file diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notice/exception/GetNoticeErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/notice/exception/GetNoticeErrorCode.java new file mode 100644 index 00000000..aa6432a9 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notice/exception/GetNoticeErrorCode.java @@ -0,0 +1,40 @@ +package com.example.matchdomain.notice.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.Objects; + +@Getter +@AllArgsConstructor +public enum GetNoticeErrorCode implements BaseErrorCode { + + @ExplainError("해당 공지사항이 존재하지 않습니다.") + NOT_EXIST_NOTICE(HttpStatus.BAD_REQUEST,false,"NOTICE_001","해당 공지사항이 존재하지 않습니다.."); + + private final HttpStatus httpStatus; + private final boolean isSuccess; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.builder().message(message).code(code).isSuccess(false).build(); + } + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notice/repository/NoticeContentRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/notice/repository/NoticeContentRepository.java new file mode 100644 index 00000000..350aab02 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notice/repository/NoticeContentRepository.java @@ -0,0 +1,8 @@ +package com.example.matchdomain.notice.repository; + +import com.example.matchdomain.notice.entity.NoticeContent; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NoticeContentRepository extends JpaRepository { + +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notice/repository/NoticeRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/notice/repository/NoticeRepository.java new file mode 100644 index 00000000..ff277b70 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notice/repository/NoticeRepository.java @@ -0,0 +1,10 @@ +package com.example.matchdomain.notice.repository; + +import com.example.matchdomain.notice.entity.Notice; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NoticeRepository extends JpaRepository { + Page findAllByOrderByCreatedAtDesc(Pageable pageable); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notification/adaptor/NotificationAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/notification/adaptor/NotificationAdaptor.java new file mode 100644 index 00000000..c614a019 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notification/adaptor/NotificationAdaptor.java @@ -0,0 +1,42 @@ +package com.example.matchdomain.notification.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchdomain.notification.entity.Notification; +import com.example.matchdomain.notification.repository.NotificationRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import static com.example.matchdomain.notification.exception.GetNotificationErrorCode.NOT_EXITS_NOTIFICATION; + +@Adaptor +@RequiredArgsConstructor +public class NotificationAdaptor { + private final NotificationRepository notificationRepository; + + public Page findByUser(User user, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + return notificationRepository.findByUserOrderByCreatedAt(user, pageable); + } + + public int countByUnRead(User user) { + return notificationRepository.countByUserAndIsRead(user, false); + } + + public void saveNotification(Notification notification) { + notificationRepository.save(notification); + } + + public Notification findNotification(Long notificationId) { + return notificationRepository.findById(notificationId).orElseThrow(() -> new BadRequestException(NOT_EXITS_NOTIFICATION)); + } + + public void readNotification(Notification notification) { + notification.setRead(true); + notificationRepository.save(notification); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notification/entity/Notification.java b/Match-Domain/src/main/java/com/example/matchdomain/notification/entity/Notification.java new file mode 100644 index 00000000..0c10dd81 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notification/entity/Notification.java @@ -0,0 +1,64 @@ +package com.example.matchdomain.notification.entity; + +import com.example.matchdomain.common.model.BaseEntity; +import com.example.matchdomain.donation.entity.DonationUser; +import com.example.matchdomain.event.entity.Event; +import com.example.matchdomain.notification.enums.NotificationType; +import com.example.matchdomain.user.entity.User; +import lombok.*; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.*; + +@Entity +@Table(name = "Notification") +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@DynamicUpdate +@DynamicInsert +@BatchSize(size = 100) +public class Notification extends BaseEntity { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "userId",nullable = false, insertable=false, updatable=false) + private User user; + + @Column(name="userId") + private Long userId; + + @Enumerated(EnumType.STRING) + private NotificationType notificationType; + + private String title; + + private String body; + + private boolean isRead = false; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "eventId",nullable = false, insertable=false, updatable=false) + private Event event; + + @Column(name="eventId") + private Long eventId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "donationUserId",nullable = false, insertable=false, updatable=false) + private DonationUser donationUser; + + @Column(name="donationUserId") + private Long donationUserId; + + public boolean getIsRead() { + return isRead; + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notification/enums/NotificationType.java b/Match-Domain/src/main/java/com/example/matchdomain/notification/enums/NotificationType.java new file mode 100644 index 00000000..e0fa702b --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notification/enums/NotificationType.java @@ -0,0 +1,17 @@ +package com.example.matchdomain.notification.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum NotificationType { + DONATION("DONATION", "기부"), + NOTICE("NOTICE", "공지"), + REGULAR_PAYMENT("REGULAR_PAYMENT", "정기결제 신청"), + TEST("TEST","테스트 알림"); + + private final String value; + + private final String type; +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notification/exception/GetNotificationErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/notification/exception/GetNotificationErrorCode.java new file mode 100644 index 00000000..09db4965 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notification/exception/GetNotificationErrorCode.java @@ -0,0 +1,40 @@ +package com.example.matchdomain.notification.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.Objects; + +@Getter +@AllArgsConstructor +public enum GetNotificationErrorCode implements BaseErrorCode { + + @ExplainError("해당 알림이 존재하지 않습니다.") + NOT_EXITS_NOTIFICATION(HttpStatus.BAD_REQUEST,false,"NOTIFICATION_001","해당 알림이 존재하지 않습니다.."); + + private final HttpStatus httpStatus; + private final boolean isSuccess; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.builder().message(message).code(code).isSuccess(false).build(); + } + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/notification/repository/NotificationRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/notification/repository/NotificationRepository.java new file mode 100644 index 00000000..2f54d445 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/notification/repository/NotificationRepository.java @@ -0,0 +1,13 @@ +package com.example.matchdomain.notification.repository; + +import com.example.matchdomain.notification.entity.Notification; +import com.example.matchdomain.user.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationRepository extends JpaRepository { + Page findByUserOrderByCreatedAt(User user, Pageable pageable); + + int countByUserAndIsRead(User user, boolean b); +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java index 8dff2328..74b5e887 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java @@ -18,7 +18,7 @@ public enum PortOneAuthErrorCode implements BaseErrorCode { FAILED_ERROR_AUTH_AMOUNT(BAD_REQUEST,"ORDER_001", "주문 가격이 일치하지 않습니다."), NOT_EXIST_ORDER_ID(BAD_REQUEST,"ORDER_002", "주문 번호가 존재하지 않습니다."), - FAILED_ERROR_AUTH(INTERNAL_SERVER_ERROR,"ORDER_003", "결제요청에 실패했습니다."); + FAILED_ERROR_AUTH(INTERNAL_SERVER_ERROR,"ORDER_003", "결제요청에 실패했습니다. 다시 시도해주세요."); private final HttpStatus httpStatus; diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectAdaptor.java new file mode 100644 index 00000000..90de255e --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectAdaptor.java @@ -0,0 +1,124 @@ +package com.example.matchdomain.project.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.constants.enums.FILTER; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.NotFoundException; +import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.project.entity.enums.ImageRepresentStatus; +import com.example.matchdomain.project.entity.enums.ProjectKind; +import com.example.matchdomain.project.exception.ProjectOneTimeErrorCode; +import com.example.matchdomain.project.repository.ProjectRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static com.example.matchdomain.common.model.Status.ACTIVE; +import static com.example.matchdomain.project.entity.enums.ImageRepresentStatus.REPRESENT; +import static com.example.matchdomain.project.entity.enums.ProjectStatus.PROCEEDING; +import static com.example.matchdomain.project.entity.enums.TodayStatus.TODAY; +import static com.example.matchdomain.project.exception.ProjectGetErrorCode.PROJECT_NOT_EXIST; + +@Adaptor +@RequiredArgsConstructor +public class ProjectAdaptor { + private final ProjectRepository projectRepository; + + public Page findLoginUserProjectList(Long userId, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + + return projectRepository.findLoginUserProjectList(userId, PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); + } + public Page findNotLoginUserProjectList(int page, int size){ + Pageable pageable = PageRequest.of(page, size); + + return projectRepository.findByStatusAndProjectStatusAndFinishedAtGreaterThanEqualAndProjectImage_ImageRepresentStatusOrderByViewCnt(ACTIVE, PROCEEDING, LocalDateTime.now(), ImageRepresentStatus.REPRESENT, pageable); + } + + public Page findLoginUserSearchProjectList(Long userId, int page, int size, String content){ + Pageable pageable = PageRequest.of(page, size); + + return projectRepository.searchProjectLoginUser(userId,content,content,content, PROCEEDING.getValue(),LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(),pageable, ACTIVE.getValue()); + } + public Page findNotLoginUserSearchProjectList(String content, int page, int size){ + Pageable pageable = PageRequest.of(page, size); + + return projectRepository.searchProject(content,content,content, PROCEEDING,LocalDateTime.now(), ImageRepresentStatus.REPRESENT,pageable, ACTIVE); + + } + + public ProjectRepository.ProjectAdminDetail projectAdminDetail(Long projectId) { + return projectRepository.getProjectAdminDetail(projectId); + } + + public Project findById(Long projectId) { + return projectRepository.findById(projectId).orElseThrow(()-> new NotFoundException(PROJECT_NOT_EXIST)); + } + + + public Page findProject(User user, int page, int size, ProjectKind projectKind, String content, FILTER filter) { + Page projects = null; + Pageable pageable = PageRequest.of(page, size); + + if(filter == FILTER.RECOMMEND) { + if (projectKind == null) { + if (content == null) { + projects = projectRepository.findLoginUserProjectList(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); + } else { + projects = projectRepository.findByContent(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), content); + + } + } else { + if (content == null) { + projects = projectRepository.findByProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue()); + + } else { + projects = projectRepository.findByContentAndProjectKind(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue(), content); + } + } + }else{ + if (projectKind == null) { + if (content == null) { + projects = projectRepository.findLoginUserProjectListLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue()); + } else { + projects = projectRepository.findByContentLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), content); + + } + } else { + if (content == null) { + projects = projectRepository.findByProjectKindLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue()); + + } else { + projects = projectRepository.findByContentAndProjectKindLatest(user.getId(), PROCEEDING.getValue(), LocalDateTime.now(), + ImageRepresentStatus.REPRESENT.getValue(), pageable, ACTIVE.getValue(), projectKind.getValue(), content); + } + } + } + + return projects; + } + + public Page getTodayProjectLists(Long userId, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + return projectRepository.findTodayProject(userId, PROCEEDING.getValue(), LocalDateTime.now(), REPRESENT.getValue(), ACTIVE.getValue(), TODAY.getValue(), pageable); + } + + public Project checkRegularProjects(Long projectId, RegularStatus regularStatus) { + return projectRepository.findByIdAndStatusAndRegularStatus(projectId, Status.ACTIVE, regularStatus).orElseThrow(() -> new BadRequestException(ProjectOneTimeErrorCode.PROJECT_NOT_EXIST)); + } + + public Optional findByProjectId(Long projectId) { + return projectRepository.findById(projectId); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectImgAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectImgAdaptor.java new file mode 100644 index 00000000..891aa1d1 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/adaptor/ProjectImgAdaptor.java @@ -0,0 +1,32 @@ +package com.example.matchdomain.project.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchdomain.project.entity.ProjectImage; +import com.example.matchdomain.project.entity.enums.ImageRepresentStatus; +import com.example.matchdomain.project.repository.ProjectImageRepository; +import com.example.matchdomain.user.entity.User; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +import static com.example.matchdomain.common.model.Status.ACTIVE; +import static com.example.matchdomain.project.exception.PatchProjectImageErrorCode.PROJECT_IMAGE_NOT_EXIST; + +@Adaptor +@RequiredArgsConstructor +public class ProjectImgAdaptor { + private final ProjectImageRepository projectImageRepository; + + public List getProjectDetail(Long projectId) { + return projectImageRepository.findByProjectIdAndImageRepresentStatusAndProject_StatusOrderBySequenceAsc(projectId, ImageRepresentStatus.NORMAL, ACTIVE); + } + + public List findProjectImages(Long projectId) { + return projectImageRepository.findByProjectIdOrderBySequenceAsc(projectId); + } + + public ProjectImage findById(Long projectImgId) { + return projectImageRepository.findById(projectImgId).orElseThrow(()-> new BadRequestException(PROJECT_IMAGE_NOT_EXIST)); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java index 93dd93ec..1da1607f 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java @@ -95,6 +95,38 @@ Page searchProjectLoginUser(@Param("userId") Long userId, @Param("c Page findLoginUserProjectList(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status); + @Query(value = "SELECT\n" + + " P.id as 'id',\n" + + " P.usages as 'usages',\n" + + " P.projectKind as 'projectKind',\n" + + " viewCnt,\n" + + " P.projectName as 'projectName',\n" + + " PI.url as 'imgUrl',\n" + + " IF((\n" + + " SELECT EXISTS (\n" + + " SELECT *\n" + + " FROM ProjectUserAttention PUA\n" + + " WHERE PUA.userId = :userId\n" + + " AND P.id = PUA.projectId\n" + + " )\n" + + " ), 'true', 'false') AS 'like',\n" + + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + + " COUNT(RP.id) AS 'totalDonationCnt'\n" + + "FROM Project P\n" + + "JOIN ProjectImage PI ON P.id = PI.projectId\n" + + "LEFT JOIN RegularPayment RP ON RP.projectId = P.id AND RP.regularPayStatus = 'PROCEEDING'\n" + + "left join User U on U.id = RP.userId " + + "WHERE PI.imageRepresentStatus = :imageRepresentStatus\n" + + "AND P.projectStatus = :projectStatus\n" + + "AND P.finishedAt >= :now\n" + + "AND P.status = :status\n" + + "GROUP BY P.id\n" + + "ORDER BY P.createdAt DESC" + , nativeQuery = true + , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status") + Page findLoginUserProjectListLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status); + @Query(value = "select P.id'projectId', " + "P.projectName, " + "usages," + @@ -122,6 +154,19 @@ Page findLoginUserProjectList(@Param("userId") Long userId, @Param( , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and projectKind = :projectKind") Page findByProjectKind(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("projectKind") String projectKind); + + @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + + "P.projectName as 'projectName', PI.url as 'imgUrl', " + + "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + + "count(RP.id)'totalDonationCnt' \n" + + "from Project P join ProjectImage PI on P.id = PI.projectId left join RegularPayment RP on RP.projectId=P.id and RP.regularPayStatus = 'PROCEEDING' " + + "left join User U on U.id = RP.userId " + + "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status and P.projectKind =:projectKind group by P.id order by P.createdAt desc" + , nativeQuery = true + , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and projectKind = :projectKind") + Page findByProjectKindLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("projectKind") String projectKind); @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + "P.projectName as 'projectName', PI.url as 'imgUrl', " + "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + @@ -140,6 +185,25 @@ Page findByProjectKind(@Param("userId") Long userId, @Param("projec Page findByContentAndProjectKind(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("projectKind") String projectKind, @Param("content") String content); + + @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + + "P.projectName as 'projectName', PI.url as 'imgUrl', " + + "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + + "count(RP.id)'totalDonationCnt' \n" + + "from Project P join ProjectImage PI on P.id = PI.projectId " + + "left join RegularPayment RP on RP.projectId=P.id and RP.regularPayStatus = 'PROCEEDING' " + + "left join User U on U.id = RP.userId " + + "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status and P.projectKind =:projectKind " + + " and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation " + + " LIKE concat('%',:content,'%') OR P.usages LIKE concat('%',:content,'%') OR P.searchKeyword LIKE concat('%',:content,'%')) group by P.id order by P.createdAt desc" + , nativeQuery = true + , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status and projectKind = :projectKind " + + "and (projectName LIKE concat('%',:content,'%') OR projectExplanation LIKE concat('%',:content,'%') " + + "OR usages LIKE concat('%',:content,'%') OR searchKeyword LIKE concat('%',:content,'%'))") + Page findByContentAndProjectKindLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, + @Param("projectKind") String projectKind, @Param("content") String content); @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + "P.projectName as 'projectName', PI.url as 'imgUrl', " + "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + @@ -160,6 +224,26 @@ Page findByContent(@Param("userId") Long userId, @Param("projectSta @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, @Param("content") String content); + @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + + "P.projectName as 'projectName', PI.url as 'imgUrl', " + + "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + + "GROUP_CONCAT(U.profileImgUrl SEPARATOR ',') AS 'imgUrlList', \n" + + "count(RP.id)'totalDonationCnt' \n" + + "from Project P " + + "join ProjectImage PI on P.id = PI.projectId " + + "left join RegularPayment RP on RP.projectId=P.id and RP.regularPayStatus = 'PROCEEDING'" + + "left join User U on U.id = RP.userId " + + "where PI.imageRepresentStatus = :imageRepresentStatus and P.projectStatus = :projectStatus and P.finishedAt>=:now and P.status = :status" + + " and (P.projectName LIKE concat('%',:content,'%') OR P.projectExplanation LIKE concat('%',:content,'%') " + + " OR P.usages LIKE concat('%',:content,'%') OR P.searchKeyword LIKE concat('%',:content,'%')) group by P.id order by P.createdAt desc" + , nativeQuery = true + , countQuery = "select * from Project where projectStatus = :projectStatus and finishedAt = :now and status = :status " + + "and (projectName LIKE concat('%',:content,'%') " + + "OR projectExplanation LIKE concat('%',:content,'%') OR usages LIKE concat('%',:content,'%') OR searchKeyword LIKE concat('%',:content,'%')) ") + Page findByContentLatest(@Param("userId") Long userId, @Param("projectStatus") String projectStatus, @Param("now") LocalDateTime now, + @Param("imageRepresentStatus") String imageRepresentStatus, Pageable pageable,@Param("status") String status, + @Param("content") String content); + @Query(value = "select P.id as 'id', P.usages as 'usages', P.projectKind as 'projectKind', viewCnt, " + "P.projectName as 'projectName', PI.url as 'imgUrl', " + "If((select exists (select * from ProjectUserAttention PUA where PUA.userId=:userId and P.id = PUA.projectId )),'true','false')'like', " + diff --git a/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java b/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java index bd2c2a4d..356bf36a 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java @@ -18,16 +18,20 @@ public class OrderRequest { private String projectId; + private int amount; + + @TimeToLive private long ttl; - public OrderRequest update(String orderId,String projectId, String userId, long ttl) { + public OrderRequest update(String orderId,String projectId, String userId, int amount, long ttl) { this.orderId = orderId; this.projectId = projectId; this.userId = userId; this.ttl = ttl; + this.amount = amount; return this; } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserAdaptor.java new file mode 100644 index 00000000..a9c07e68 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserAdaptor.java @@ -0,0 +1,18 @@ +package com.example.matchdomain.user.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchdomain.user.entity.User; +import com.example.matchdomain.user.entity.enums.SocialType; +import com.example.matchdomain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; + +import java.util.Optional; + +@Adaptor +@RequiredArgsConstructor +public class UserAdaptor { + private final UserRepository userRepository; + public Optional existsSocialUser(String socialId, SocialType socialType){ + return userRepository.findBySocialIdAndSocialType(socialId, socialType); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserCardAdaptor.java b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserCardAdaptor.java new file mode 100644 index 00000000..94f07e3b --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/adaptor/UserCardAdaptor.java @@ -0,0 +1,26 @@ +package com.example.matchdomain.user.adaptor; + +import com.example.matchcommon.annotation.Adaptor; +import com.example.matchcommon.exception.NotFoundException; +import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.entity.UserCard; +import com.example.matchdomain.donation.repository.UserCardRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +import static com.example.matchdomain.donation.exception.DeleteCardErrorCode.CARD_NOT_EXIST; + +@Adaptor +@RequiredArgsConstructor +public class UserCardAdaptor { + private final UserCardRepository userCardRepository; + + public List findCardLists(Long userId) { + return userCardRepository.findByUserIdAndStatus(userId, Status.ACTIVE); + } + + public UserCard findCardByCardId(Long cardId) { + return userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); + } +} diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java b/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java index 47b91e98..b0b68fa8 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/entity/User.java @@ -80,18 +80,22 @@ public class User extends BaseEntity implements UserDetails { @JoinColumn(name = "userId") private List userCard = new ArrayList<>(); + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "userId") + @BatchSize(size = 10) + private List userFcmTokens = new ArrayList<>(); + @Column(name = "logInAt") private LocalDateTime logInAt; - @Column(name = "role") private String role; @Enumerated(EnumType.STRING) - private Alarm serviceAlarm = Alarm.INACTIVE; + private Alarm serviceAlarm = Alarm.ACTIVE; @Enumerated(EnumType.STRING) - private Alarm eventAlarm = Alarm.INACTIVE; + private Alarm eventAlarm = Alarm.ACTIVE; @Override public Collection getAuthorities() { @@ -143,6 +147,6 @@ public boolean isActivated() { public void setModifyProfile(String newProfileImg, String name) { this.profileImgUrl = newProfileImg; - this.name = name; + this.nickname = name; } } diff --git a/Match-Domain/src/main/java/com/example/matchdomain/user/exception/AppleErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/AppleErrorCode.java new file mode 100644 index 00000000..e10a3042 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/user/exception/AppleErrorCode.java @@ -0,0 +1,44 @@ +package com.example.matchdomain.user.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.FORBIDDEN; + +@Getter +@AllArgsConstructor +public enum AppleErrorCode implements BaseErrorCode { + @ExplainError("공개키 에러 관리자에게 문위") + MISMATCH_APPLE_KEY(BAD_REQUEST,"APPLE_001","공개 키중 일치하는 키 값이 존재하지 않습니다."); + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + HashMap data = new HashMap<>(); + data.put("signUpType","이미 가입된 (가입한 타입) 계정이 존재 합니다. (가입한 타입) 계정으로 로그인 해주세요."); + return ErrorReason.builder().message(message).code(code).isSuccess(false).result(data).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Domain/src/main/resources/application-domain-dev.yml b/Match-Domain/src/main/resources/application-domain-dev.yml index 978a0c0c..218d935c 100644 --- a/Match-Domain/src/main/resources/application-domain-dev.yml +++ b/Match-Domain/src/main/resources/application-domain-dev.yml @@ -9,10 +9,18 @@ spring: init: platform: mysql datasource: - url: ${DB_URL_HOST} - username: ${AWS_DB_USER_NAME} - password: ${AWS_DB_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver + master: + hikari: + username: ${AWS_DB_USER_NAME} + password: ${AWS_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: ${DB_URL_HOST} + slave: + hikari: + username: ${AWS_DB_USER_NAME} + password: ${AWS_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: ${DEV_SLAVE_URL_HOST} jpa: database-platform: org.hibernate.dialect.MySQL5InnoDBDialect # create, update, create-drop, none 등의 옵션이 있습니다. diff --git a/Match-Domain/src/main/resources/application-domain-localDev.yml b/Match-Domain/src/main/resources/application-domain-localDev.yml index f73348fd..1fe03d90 100644 --- a/Match-Domain/src/main/resources/application-domain-localDev.yml +++ b/Match-Domain/src/main/resources/application-domain-localDev.yml @@ -9,10 +9,18 @@ spring: init: platform: mysql datasource: - url: ${DB_URL_HOST} - username: ${AWS_DB_USER_NAME} - password: ${AWS_DB_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver + master: + hikari: + username: ${AWS_DB_USER_NAME} + password: ${AWS_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: ${DB_URL_HOST} + slave: + hikari: + username: ${AWS_DB_USER_NAME} + password: ${AWS_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: ${DEV_SLAVE_URL_HOST} jpa: database-platform: org.hibernate.dialect.MySQL5InnoDBDialect # create, update, create-drop, none 등의 옵션이 있습니다. diff --git a/Match-Domain/src/main/resources/application-domain-prod.yml b/Match-Domain/src/main/resources/application-domain-prod.yml index d511f5fa..4411b518 100644 --- a/Match-Domain/src/main/resources/application-domain-prod.yml +++ b/Match-Domain/src/main/resources/application-domain-prod.yml @@ -9,9 +9,18 @@ spring: init: platform: mysql datasource: - url: ${PROD_DB_URL_HOST} - username: ${AWS_DB_USER_NAME} - password: ${AWS_DB_PASSWORD} + master: + hikari: + username: ${AWS_DB_USER_NAME} + password: ${AWS_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: ${PROD_DB_URL_HOST} + slave: + hikari: + username: ${AWS_DB_USER_NAME} + password: ${AWS_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: ${PROD_SLAVE_URL_HOST} driver-class-name: com.mysql.cj.jdbc.Driver jpa: database-platform: org.hibernate.dialect.MySQL5InnoDBDialect diff --git a/Match-Domain/src/main/resources/application-domain.yml b/Match-Domain/src/main/resources/application-domain.yml index 8440a50f..731621c7 100644 --- a/Match-Domain/src/main/resources/application-domain.yml +++ b/Match-Domain/src/main/resources/application-domain.yml @@ -1,6 +1,6 @@ spring: redis: - host: ${REDIS_DEV_HOST} + host: localhost port: 6379 config: activate: @@ -9,19 +9,22 @@ spring: init: platform: mysql datasource: - url: ${LOCAL_DB_URL_HOST} - username: ${LOCAL_DB_USER_NAME} - password: ${LOCAL_DB_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver + master: + hikari: + username: root + password: 111 + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: jdbc:mysql://localhost:4406/mydb + slave: + hikari: + username: root + password: 111 + driver-class-name: com.mysql.cj.jdbc.Driver + jdbc-url: jdbc:mysql://localhost:5506/mydb jpa: database-platform: org.hibernate.dialect.MySQL5InnoDBDialect - # create, update, create-drop, none 등의 옵션이 있습니다. - # create: 기존테이블 삭제 후 다시 생성 - # update: 변경된 부분만 반영 - # create-drop: create와 같으나 종료 시점에 테이블 DROP - # none: 사용하지 않음 hibernate: - ddl-auto: create + ddl-auto: update naming: implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/FeignCommonConfig.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/FeignCommonConfig.java index 60ccef62..3667b1ab 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/FeignCommonConfig.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/FeignCommonConfig.java @@ -9,6 +9,7 @@ import com.example.matchinfrastructure.oauth.BaseFeignClientPackage; import com.example.matchinfrastructure.pay.BasePayFeignClientPackage; import com.example.matchinfrastructure.pay.nice.client.NiceAuthFeignClient; +import com.example.matchinfrastructure.user.UserFeignClientPackage; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -24,7 +25,8 @@ @Configuration @EnableFeignClients(basePackageClasses = {BaseFeignClientPackage.class, BasePayFeignClientPackage.class, - BaseFeignDiscordPackage.class, BaseFeignAligoPackage.class, BaseFeignMatchAligoPackage.class}) + BaseFeignDiscordPackage.class, BaseFeignAligoPackage.class, BaseFeignMatchAligoPackage.class, + UserFeignClientPackage.class}) public class FeignCommonConfig { @Bean diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/s3/S3UploadService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/s3/S3UploadService.java index 3b76ea32..b8894263 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/s3/S3UploadService.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/config/s3/S3UploadService.java @@ -1,9 +1,7 @@ package com.example.matchinfrastructure.config.s3; -import com.amazonaws.HttpMethod; import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.Headers; import com.amazonaws.services.s3.model.*; import com.example.matchcommon.exception.BadRequestException; import com.example.matchcommon.exception.ForbiddenException; @@ -11,16 +9,15 @@ import com.example.matchcommon.properties.AwsS3Properties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.util.ArrayList; -import java.util.Date; import java.util.List; +import java.util.Objects; import java.util.UUID; import static com.example.matchcommon.exception.errorcode.FileUploadException.*; @@ -150,8 +147,6 @@ public void deleteFile(String fileName){ boolean isObjectExist = amazonS3.doesObjectExist(awsS3Properties.getS3().getBucket(), fileRoute); if (isObjectExist) { amazonS3.deleteObject(awsS3Properties.getS3().getBucket(),fileRoute); - } else { - throw new InternalServerException(IMAGE_DELETE_ERROR); } } catch (Exception e) { throw new InternalServerException(IMAGE_DELETE_ERROR); @@ -173,4 +168,63 @@ public String uploadProfilePresentFile(Long userId,MultipartFile presentFile) { } return amazonS3.getUrl(awsS3Properties.getS3().getBucket(), fileName).toString(); } + + public String uploadBannerImage(MultipartFile bannerImage) { + String fileName = getForBannerFileName(getFileExtension(bannerImage.getOriginalFilename())); + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(bannerImage.getSize()); + objectMetadata.setContentType(bannerImage.getContentType()); + + try (InputStream inputStream = bannerImage.getInputStream()) { + amazonS3.putObject(new PutObjectRequest(awsS3Properties.getS3().getBucket(), fileName, inputStream, objectMetadata).withCannedAcl(CannedAccessControlList.PublicRead)); + } catch (IOException e) { + throw new ForbiddenException(IMAGE_UPLOAD_ERROR); + } + return amazonS3.getUrl(awsS3Properties.getS3().getBucket(), fileName).toString(); + } + + private String getForBannerFileName(String fileExtension) { + return "banner/" + + UUID.randomUUID() + + fileExtension; + } + + public String uploadByteCode(String s3FileName, byte[] thumbnailBytes) { + ObjectMetadata metadata = new ObjectMetadata(); + + metadata.setContentLength(thumbnailBytes.length); + metadata.setContentType("image/jpeg"); + + amazonS3.putObject(new PutObjectRequest(awsS3Properties.getS3().getBucket(),s3FileName,new ByteArrayInputStream(thumbnailBytes),metadata)); + + return amazonS3.getUrl(awsS3Properties.getS3().getBucket(),s3FileName).toString(); + } + + + public String uploadByteCodeOne(String dirName, byte[] thumbnailBytes) { + String s3FileName=dirName+"/"+UUID.randomUUID().toString()+".jpg"; + + return uploadByteCode(s3FileName, thumbnailBytes); + } + + public String uploadOneImg(String dirName, MultipartFile imgFile) { + String fileName = getForOneFileName(dirName, getFileExtension(Objects.requireNonNull(imgFile.getOriginalFilename()))); + + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(imgFile.getSize()); + objectMetadata.setContentType(imgFile.getContentType()); + + try (InputStream inputStream = imgFile.getInputStream()) { + amazonS3.putObject(new PutObjectRequest(awsS3Properties.getS3().getBucket(), fileName, inputStream, objectMetadata).withCannedAcl(CannedAccessControlList.PublicRead)); + } catch (IOException e) { + throw new ForbiddenException(IMAGE_UPLOAD_ERROR); + } + return amazonS3.getUrl(awsS3Properties.getS3().getBucket(), fileName).toString(); + } + + private String getForOneFileName(String dirName, String fileExtension) { + return dirName+"/" + + UUID.randomUUID() + + fileExtension; + } } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/client/DiscordFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/client/DiscordFeignClient.java index 3ff57d7d..07367712 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/client/DiscordFeignClient.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/client/DiscordFeignClient.java @@ -4,6 +4,7 @@ import com.example.matchinfrastructure.discord.config.DiscordInfoConfig; import com.example.matchinfrastructure.discord.dto.Message; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PostMapping; diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/convertor/DiscordConvertor.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/convertor/DiscordConvertor.java index a6cb8bbf..5a8cd855 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/convertor/DiscordConvertor.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/convertor/DiscordConvertor.java @@ -4,7 +4,6 @@ import com.example.matchinfrastructure.discord.dto.Message; import lombok.RequiredArgsConstructor; import org.springframework.core.env.Environment; -import org.springframework.security.core.userdetails.User; import javax.servlet.http.HttpServletRequest; import java.text.DecimalFormat; @@ -19,12 +18,8 @@ public class DiscordConvertor { private final Environment environment; - public Message Message(User user, Exception exception, HttpServletRequest request) { + public Message convertToUnknownMessage(String username, Exception exception, HttpServletRequest request) { List embedsList = new ArrayList<>(); - String username = "로그인 되지 않은 유저"; - if(user!=null){ - username = user.getUsername(); - } embedsList.add(Message.Embeds.builder().title("실행중인 환경").description(Arrays.toString(environment.getActiveProfiles())).build()); embedsList.add(Message.Embeds.builder().title("에러 내용").description(exception.getMessage()).build()); @@ -40,7 +35,7 @@ public Message Message(User user, Exception exception, HttpServletRequest reques .build(); } - public Message AlertBatchMessage(String title, int size) { + public Message convertToAlertBatchMessage(String title, int size) { LocalDateTime localDateTime = LocalDateTime.now(); List embedsList = new ArrayList<>(); embedsList.add(Message.Embeds.builder().title("총 결제 예정 수").description(String.valueOf(size)).build()); @@ -63,7 +58,7 @@ public Message AlertBatchMessage(String title, int size) { .build(); } - public Message AlertFinishMessage(String title, int amount, int size, int successCnt, int trueCnt) { + public Message convertToAlertFinishMessage(String title, int amount, int size, int successCnt, int trueCnt) { LocalDateTime localDateTime = LocalDateTime.now(); List embedsList = new ArrayList<>(); DecimalFormat decimalFormat = new DecimalFormat("#,###"); // 포맷을 설정합니다. @@ -94,7 +89,7 @@ public Message AlertFinishMessage(String title, int amount, int size, int succes .build(); } - public Message ErrorBatchServer(String title, String message) { + public Message convertToErrorBatchServer(String title, String message) { List embedsList = new ArrayList<>(); embedsList.add(Message.Embeds.builder().title("실행중인 환경").description(Arrays.toString(environment.getActiveProfiles())).build()); @@ -109,4 +104,17 @@ public Message ErrorBatchServer(String title, String message) { .embeds(embedsList) .build(); } + + public Message convertToKnownMessage(String message) { + List embedsList = new ArrayList<>(); + + embedsList.add(Message.Embeds.builder().title("실행중인 환경").description(Arrays.toString(environment.getActiveProfiles())).build()); + + return Message + .builder() + .content(message) + .tts(false) + .embeds(embedsList) + .build(); + } } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/service/DiscordService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/service/DiscordService.java new file mode 100644 index 00000000..7730e6ec --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/service/DiscordService.java @@ -0,0 +1,26 @@ +package com.example.matchinfrastructure.discord.service; + +import com.example.matchinfrastructure.discord.client.DiscordFeignClient; +import com.example.matchinfrastructure.discord.convertor.DiscordConvertor; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +@Service +@RequiredArgsConstructor +public class DiscordService { + private final DiscordFeignClient discordFeignClient; + private final DiscordConvertor discordConvertor; + @Async("discord-message") + public void sendUnKnownMessage(String username, Exception exception, HttpServletRequest request) { + discordFeignClient.errorMessage(discordConvertor.convertToUnknownMessage(username, exception, request)); + + } + + @Async("discord-message") + public void sendKnownErrorMessage(String message) { + discordFeignClient.errorMessage(discordConvertor.convertToKnownMessage(message)); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/convertor/FcmConvertor.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/convertor/FcmConvertor.java new file mode 100644 index 00000000..2f0531e2 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/convertor/FcmConvertor.java @@ -0,0 +1,8 @@ +package com.example.matchinfrastructure.fcm.convertor; + +import com.example.matchcommon.annotation.Convertor; + +@Convertor +public class FcmConvertor { + +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/FCMNotificationRequestDto.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/FCMNotificationRequestDto.java new file mode 100644 index 00000000..11c41119 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/dto/FCMNotificationRequestDto.java @@ -0,0 +1,16 @@ +package com.example.matchinfrastructure.fcm.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class FCMNotificationRequestDto { + private String title; + + private String body; + + private String token; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/service/FcmNotificationService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/service/FcmNotificationService.java new file mode 100644 index 00000000..ab795d43 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/fcm/service/FcmNotificationService.java @@ -0,0 +1,68 @@ +package com.example.matchinfrastructure.fcm.service; + +import com.example.matchinfrastructure.fcm.dto.FCMNotificationRequestDto; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.Message; +import com.google.firebase.messaging.Notification; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.util.List; + +@Service +@RequiredArgsConstructor +@Slf4j +public class FcmNotificationService { + @PostConstruct + public void init() { + log.info("init"); + try { + FirebaseOptions options = new FirebaseOptions.Builder() + .setCredentials( + GoogleCredentials + .fromStream(new ClassPathResource("serviceAccountKey.json").getInputStream()) + .createScoped(List.of("https://www.googleeapis.com/auth/cloud-platform"))) + .build(); + if (FirebaseApp.getApps().isEmpty()) { + FirebaseApp.initializeApp(options); + log.info("Firebase application has been initialized"); + } + } catch (IOException e) { + log.info(e.getMessage()); + log.info("FireBase Config Failed"); + // spring 뜰때 알림 서버가 잘 동작하지 않는 것이므로 바로 죽임 + throw new RuntimeException(e.getMessage()); + + } + } + + @Async("fcm") + public void testNotification(FCMNotificationRequestDto fcmNotificationRequestDto){ + Notification notification = Notification + .builder() + .setTitle(fcmNotificationRequestDto.getTitle()) + .setBody(fcmNotificationRequestDto.getBody()) + .build(); + + Message message = Message + .builder() + .setToken(fcmNotificationRequestDto.getToken()) + .setNotification(notification) + .build(); + try { + FirebaseMessaging.getInstance().send(message); + } catch (FirebaseMessagingException e) { + throw new RuntimeException(e); + } + + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/client/AppleFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/client/AppleFeignClient.java new file mode 100644 index 00000000..05b65665 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/client/AppleFeignClient.java @@ -0,0 +1,18 @@ +package com.example.matchinfrastructure.oauth.apple.client; + +import com.example.matchinfrastructure.oauth.apple.config.AppleInfoConfig; +import com.example.matchinfrastructure.oauth.apple.dto.ApplePublicResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient( + name = "AppleFeignClient", + url = "https://appleid.apple.com", + configuration = AppleInfoConfig.class) +@Component +public interface AppleFeignClient { + @GetMapping("/auth/keys") + ApplePublicResponse getPublicKey(); +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleFeignConfiguration.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleFeignConfiguration.java new file mode 100644 index 00000000..f5493410 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleFeignConfiguration.java @@ -0,0 +1,25 @@ +package com.example.matchinfrastructure.oauth.apple.config; + +import com.example.matchinfrastructure.oauth.kakao.config.KakaoInfoErrorDecoder; +import feign.Logger; +import feign.RequestInterceptor; +import feign.codec.ErrorDecoder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(AppleInfoErrorDecoder.class) +public class AppleFeignConfiguration { + @Bean + public RequestInterceptor requestInterceptor() { + return template -> template.header("Content-Type", "application/json"); + } + @Bean + public ErrorDecoder errorDecoder() { + return new AppleInfoErrorDecoder(); + } + + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleInfoConfig.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleInfoConfig.java new file mode 100644 index 00000000..ced15c9f --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleInfoConfig.java @@ -0,0 +1,24 @@ +package com.example.matchinfrastructure.oauth.apple.config; + + +import com.example.matchinfrastructure.oauth.kakao.config.KakaoInfoErrorDecoder; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(AppleInfoErrorDecoder.class) +public class AppleInfoConfig { + + @Bean + @ConditionalOnMissingBean(value = ErrorDecoder.class) + public AppleInfoErrorDecoder commonFeignErrorDecoder() { + return new AppleInfoErrorDecoder(); + } + + @Bean + Encoder formEncoder() { + return new feign.form.FormEncoder(); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleInfoErrorDecoder.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleInfoErrorDecoder.java new file mode 100644 index 00000000..475dfd7a --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/config/AppleInfoErrorDecoder.java @@ -0,0 +1,30 @@ +package com.example.matchinfrastructure.oauth.apple.config; + + +import com.example.matchcommon.exception.OtherServerException; +import feign.FeignException; +import feign.Response; +import feign.codec.ErrorDecoder; + +import static com.example.matchcommon.exception.errorcode.OtherServerErrorCode.*; + +public class AppleInfoErrorDecoder implements ErrorDecoder { + + @Override + public Exception decode(String methodKey, Response response) { + if (response.status() >= 400) { + switch (response.status()) { + case 401: + throw new OtherServerException(OTHER_SERVER_UNAUTHORIZED); + case 403: + throw new OtherServerException(OTHER_SERVER_FORBIDDEN); + case 419: + throw new OtherServerException(OTHER_SERVER_EXPIRED_TOKEN); + default: + throw new OtherServerException(OTHER_SERVER_BAD_REQUEST); + } + } + + return FeignException.errorStatus(methodKey, response); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/ApplePublicResponse.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/ApplePublicResponse.java new file mode 100644 index 00000000..1bdc9d35 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/ApplePublicResponse.java @@ -0,0 +1,17 @@ +package com.example.matchinfrastructure.oauth.apple.dto; + + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; +import java.util.Optional; + +@Getter +@Setter +@NoArgsConstructor +public class ApplePublicResponse { + private List keys; +} + diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/AppleUserRes.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/AppleUserRes.java new file mode 100644 index 00000000..a276ec7d --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/AppleUserRes.java @@ -0,0 +1,14 @@ +package com.example.matchinfrastructure.oauth.apple.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class AppleUserRes { + private String email; + + private String socialId; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/Key.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/Key.java new file mode 100644 index 00000000..2f0131e1 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/dto/Key.java @@ -0,0 +1,17 @@ +package com.example.matchinfrastructure.oauth.apple.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class Key { + private String kty; + private String kid; + private String use; + private String alg; + private String n; + private String e; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/exception/AppleErrorCode.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/exception/AppleErrorCode.java new file mode 100644 index 00000000..c3f2de68 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/exception/AppleErrorCode.java @@ -0,0 +1,45 @@ +package com.example.matchinfrastructure.oauth.apple.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +@Getter +@AllArgsConstructor +public enum AppleErrorCode implements BaseErrorCode { + @ExplainError("공개키 에러 관리자에게 문위") + MISMATCH_APPLE_KEY(BAD_REQUEST,"APPLE_001","공개 키중 일치하는 키 값이 존재하지 않습니다."), + FAIL_MAKE_PUBLIC_KEY(BAD_REQUEST,"APPLE_002", "공개키를 만드는데 실패했습니다."), + APPLE_BAD_REQUEST(BAD_REQUEST,"APPLE_003","인증에 실패했습니다."); + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + HashMap data = new HashMap<>(); + data.put("signUpType","이미 가입된 (가입한 타입) 계정이 존재 합니다. (가입한 타입) 계정으로 로그인 해주세요."); + return ErrorReason.builder().message(message).code(code).isSuccess(false).result(data).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/service/AppleAuthService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/service/AppleAuthService.java new file mode 100644 index 00000000..8d066737 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/oauth/apple/service/AppleAuthService.java @@ -0,0 +1,95 @@ +package com.example.matchinfrastructure.oauth.apple.service; + +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchinfrastructure.oauth.apple.client.AppleFeignClient; +import com.example.matchinfrastructure.oauth.apple.dto.ApplePublicResponse; +import com.example.matchinfrastructure.oauth.apple.dto.AppleUserRes; +import com.example.matchinfrastructure.oauth.apple.dto.Key; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.RSAPublicKeySpec; +import java.util.Base64; +import java.util.List; +import java.util.Objects; + +import static com.example.matchinfrastructure.oauth.apple.exception.AppleErrorCode.*; + +@Service +@RequiredArgsConstructor +public class AppleAuthService { + private final AppleFeignClient appleFeignClient; + public AppleUserRes appleLogin(String identityToken) { + JsonParser parser = new JsonParser(); + ApplePublicResponse applePublicResponse = appleFeignClient.getPublicKey(); + + System.out.println(applePublicResponse.getKeys().get(0).getKid()); + + String headerOfIdentityToken = identityToken.substring(0, identityToken.indexOf(".")); + + String header = new String(Base64.getDecoder().decode(headerOfIdentityToken), StandardCharsets.UTF_8); + + JsonObject parseHeader = (new Gson()).fromJson(header, JsonObject.class); + String kidValue = parseHeader.get("kid").getAsString(); + String algValue = parseHeader.get("alg").getAsString(); + Key foundKey = getMatchedKeyBy(kidValue, algValue, applePublicResponse.getKeys()); + + PublicKey publicKey = getPublicKey(foundKey); + + Claims userInfo = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(identityToken).getBody(); + JsonObject userInfoObject = (JsonObject) parser.parse(new Gson().toJson(userInfo)); + String iss = userInfoObject.get("iss").getAsString(); + String aud = userInfoObject.get("aud").getAsString(); + + checkValidationInfo(iss, aud); + + String appleId = userInfoObject.get("sub").getAsString(); + String email = userInfoObject.get("email").getAsString(); + + return new AppleUserRes(email, appleId); + } + + private void checkValidationInfo(String iss, String aud) { + if (!Objects.equals(iss, "https://appleid.apple.com")) + throw new BadRequestException(APPLE_BAD_REQUEST); + + if (!Objects.equals(aud, "com.dev.match")) + throw new BadRequestException(APPLE_BAD_REQUEST); + } + + + public Key getMatchedKeyBy(String kid, String alg, List keys) { + return keys.stream() + .filter(key -> key.getKid().equals(kid) && key.getAlg().equals(alg)) + .findFirst().orElseThrow(()->new BadRequestException(MISMATCH_APPLE_KEY)); + } + + + public PublicKey getPublicKey(Key key) { + String nStr = key.getN(); + String eStr = key.getE(); + + byte[] nBytes = Base64.getUrlDecoder().decode(nStr.substring(0)); + byte[] eBytes = Base64.getUrlDecoder().decode(eStr.substring(0)); + + BigInteger n = new BigInteger(1, nBytes); + BigInteger e = new BigInteger(1, eBytes); + + try { + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n, e); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(publicKeySpec); + } catch (Exception exception) { + throw new BadRequestException(FAIL_MAKE_PUBLIC_KEY); + } + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java index be91a0d0..5130c153 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java @@ -5,13 +5,11 @@ import com.example.matchinfrastructure.pay.portone.config.PortOneFeignConfiguration; import com.example.matchinfrastructure.pay.portone.config.PortOneInfoConfig; import com.example.matchinfrastructure.pay.portone.config.PortOneInfoErrorDecoder; -import com.example.matchinfrastructure.pay.portone.dto.PortOneAuth; -import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; -import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; -import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.dto.*; import com.example.matchinfrastructure.pay.portone.dto.req.PayWithBillKeyReq; import com.example.matchinfrastructure.pay.portone.dto.req.PortOneAuthReq; import com.example.matchinfrastructure.pay.portone.dto.req.PortOneBillReq; +import com.example.matchinfrastructure.pay.portone.dto.req.PortOnePrepareReq; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.*; @@ -36,4 +34,16 @@ public PortOneResponse payWithBillKey( @RequestHeader("Authorization") String accessToken, @RequestBody PayWithBillKeyReq payWithBillKeyReq ); + + @DeleteMapping("/subscribe/customers/{customer_uid}") + public PortOneResponse deleteBillKey( + @RequestHeader("Authorization") String accessToken, + @PathVariable("customer_uid") String customer_uid + ); + + @PostMapping("/payments/prepare") + public PortOneResponse preparePayments( + @RequestHeader("Authorization") String accessToken, + @RequestBody PortOnePrepareReq portOnePrepareReq + ); } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java index a4f3bef6..17c4fcbf 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java @@ -4,13 +4,14 @@ import com.example.matchcommon.properties.PortOneProperties; import com.example.matchinfrastructure.pay.portone.dto.req.PayWithBillKeyReq; import com.example.matchinfrastructure.pay.portone.dto.req.PortOneBillReq; +import com.example.matchinfrastructure.pay.portone.dto.req.PortOnePrepareReq; import lombok.RequiredArgsConstructor; @Convertor @RequiredArgsConstructor public class PortOneConvertor { private final PortOneProperties portOneProperties; - public PortOneBillReq PortOneBill(String cardNo, String expiry, String idNo, String cardPw) { + public PortOneBillReq convertToPortOneBill(String cardNo, String expiry, String idNo, String cardPw) { return PortOneBillReq .builder() .pg("nice."+portOneProperties.getBillmid()) @@ -21,7 +22,7 @@ public PortOneBillReq PortOneBill(String cardNo, String expiry, String idNo, Str .build(); } - public PayWithBillKeyReq PayWithBillKey(String bid, String orderId, Long amount, String projectName, String customerId) { + public PayWithBillKeyReq convertPayWithBillKey(String bid, String orderId, Long amount, String projectName, String customerId) { return PayWithBillKeyReq .builder() .customer_uid(bid) @@ -30,4 +31,12 @@ public PayWithBillKeyReq PayWithBillKey(String bid, String orderId, Long amount, .name(projectName) .build(); } + + public PortOnePrepareReq convertToRequestPrepare(String orderId, int amount) { + return PortOnePrepareReq + .builder() + .merchant_uid(orderId) + .amount(amount) + .build(); + } } diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillDeleteResponse.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillDeleteResponse.java new file mode 100644 index 00000000..7729938d --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillDeleteResponse.java @@ -0,0 +1,28 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class PortOneBillDeleteResponse { + private String customer_uid; // 구매자의 결제 수단 식별 고유번호 + private String pg_provider; // PG사 구분코드 + private String pg_id; // PG사 상점아이디(MID) + private String customer_id; // 구매자 ID + private String card_name; // 빌링키 발급 한 카드명 + private String card_code; // 카드사 코드 + private String card_number; // 마스킹된 카드번호 + private Integer card_type; // 카드유형 + private String customer_name; // 고객 성함 + private String customer_tel; // 전화번호 + private String customer_email; // Email 주소 + private String customer_addr; // 주소 + private String customer_postcode; // 우편번호 + private Integer inserted; // 빌링키 발급 시각 UNIX timestamp + private Integer updated; // 빌링키 업데이트 시각 UNIX timestamp +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOnePrepareRes.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOnePrepareRes.java new file mode 100644 index 00000000..22700de7 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOnePrepareRes.java @@ -0,0 +1,14 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PortOnePrepareRes { + private String merchant_uid; + + private double amount; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOnePrepareReq.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOnePrepareReq.java new file mode 100644 index 00000000..68cb10dc --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOnePrepareReq.java @@ -0,0 +1,14 @@ +package com.example.matchinfrastructure.pay.portone.dto.req; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PortOnePrepareReq { + private String merchant_uid; + + private double amount; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java index 28117605..613f8da7 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java @@ -18,32 +18,19 @@ public class PortOneAuthService { private final PortOneFeignClient portOneFeignClient; private final PortOneProperties portOneProperties; + @Cacheable(value = "portOneTokenCache", key = "'all'") public String getToken() { String token = fetchPortOneToken(); - System.out.println(token); + System.out.println("request : " + token); return token; } - @CachePut(value = "portOneTokenCache") public String fetchPortOneToken() { - PortOneResponse portOneResponse = portOneFeignClient.getAccessToken(PortOneAuthReq.builder().imp_key(portOneProperties.getKey()).imp_secret(portOneProperties.getSecret()).build()); - return portOneResponse.getResponse().getAccess_token(); - } - - @CachePut(value = "portOneTokenCache") - public String refreshToken() { - String newToken = fetchPortOneToken(); - System.out.println("refresh Token"); - return newToken; - } - - //@Scheduled(fixedRate = 100) - @Scheduled(fixedRate = 1700000) // 30분마다 실행 (1800초) - public void scheduleTokenRefresh() { - System.out.println("refresh token Schedule"); - String refreshToken = refreshToken(); + System.out.println("request token"); + return getTokens(); } + @CachePut(value = "portOneTokenCache", key = "'all'") public String getTokens() { PortOneResponse portOneResponse = portOneFeignClient.getAccessToken(PortOneAuthReq.builder().imp_key(portOneProperties.getKey()).imp_secret(portOneProperties.getSecret()).build()); return portOneResponse.getResponse().getAccess_token(); diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneService.java new file mode 100644 index 00000000..8b820159 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneService.java @@ -0,0 +1,15 @@ +package com.example.matchinfrastructure.pay.portone.service; + +import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; +import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import static com.example.matchcommon.constants.MatchStatic.REGULAR; + +@Service +@RequiredArgsConstructor +public class PortOneService { +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/UserFeignClientPackage.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/UserFeignClientPackage.java new file mode 100644 index 00000000..fa667b0d --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/UserFeignClientPackage.java @@ -0,0 +1,3 @@ +package com.example.matchinfrastructure.user; + +public interface UserFeignClientPackage {} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/client/NickNameFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/client/NickNameFeignClient.java new file mode 100644 index 00000000..1aaa8bea --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/client/NickNameFeignClient.java @@ -0,0 +1,17 @@ +package com.example.matchinfrastructure.user.client; + +import com.example.matchinfrastructure.user.dto.NickNameRes; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; + +@FeignClient( + name = "NickNameFeignClient", + url = "https://nickname.hwanmoo.kr" +) +@Component +public interface NickNameFeignClient { + + @GetMapping(value = "/?format=json&count=1&max_length=8") + NickNameRes getNickName(); +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/dto/NickNameRes.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/dto/NickNameRes.java new file mode 100644 index 00000000..df8c6b2a --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/user/dto/NickNameRes.java @@ -0,0 +1,10 @@ +package com.example.matchinfrastructure.user.dto; + +import lombok.Getter; + +import java.util.List; + +@Getter +public class NickNameRes { + private List words; +} diff --git a/Match-Infrastructure/src/main/resources/application-infrastructure.yml b/Match-Infrastructure/src/main/resources/application-infrastructure.yml index ab58f428..fa2d047b 100644 --- a/Match-Infrastructure/src/main/resources/application-infrastructure.yml +++ b/Match-Infrastructure/src/main/resources/application-infrastructure.yml @@ -3,11 +3,6 @@ feign: config: default: loggerLevel: FULL -spring: - cache: - cache-names: portOneTokenCache - caffeine: - spec: maximumSize=100,expireAfterWrite=1700s # 30분마다 갱신 --- spring: config: diff --git a/build.gradle b/build.gradle index b25aae15..8dd100c4 100644 --- a/build.gradle +++ b/build.gradle @@ -43,9 +43,18 @@ subprojects{ implementation 'org.springframework.boot:spring-boot-starter' testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'net.sf.ehcache:ehcache:2.10.3' + + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + //map struct + implementation 'org.mapstruct:mapstruct:1.5.3.Final' + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' + } tasks.named('test') {