diff --git a/resource-server/src/main/java/com/inhabas/api/config/SwaggerConfig.java b/resource-server/src/main/java/com/inhabas/api/config/SwaggerConfig.java index 942e5c35..c7df315e 100644 --- a/resource-server/src/main/java/com/inhabas/api/config/SwaggerConfig.java +++ b/resource-server/src/main/java/com/inhabas/api/config/SwaggerConfig.java @@ -94,7 +94,7 @@ public GroupedOpenApi getIBASApi() { return GroupedOpenApi.builder() .group("IBAS 관련") - .pathsToMatch("/club/**", "/**/**/**/comment/**", "/**/**/**/comments") + .pathsToMatch("/club/**", "/**/**/**/comment/**", "/**/**/**/comments", "/file/upload/**") .build(); } @@ -103,14 +103,17 @@ public GroupedOpenApi getBoardApi() { return GroupedOpenApi.builder() .group("게시판 관련") - .pathsToMatch("/board/**", "/**/**/**/comment/**", "/**/**/**/comments") + .pathsToMatch("/board/**", "/**/**/**/comment/**", "/**/**/**/comments", "/file/upload/**") .build(); } @Bean public GroupedOpenApi getBudgetApi() { - return GroupedOpenApi.builder().group("회계 관련").pathsToMatch("/budget/**").build(); + return GroupedOpenApi.builder() + .group("회계 관련") + .pathsToMatch("/budget/**", "/file/upload/**") + .build(); } @Bean @@ -118,7 +121,8 @@ public GroupedOpenApi getProjectBoardApi() { return GroupedOpenApi.builder() .group("프로젝트 게시판 관련") - .pathsToMatch("/project/**", "/**/**/**/comment/**", "/**/**/**/comments") + .pathsToMatch( + "/project/**", "/**/**/**/comment/**", "/**/**/**/comments", "/file/upload/**") .build(); } @@ -127,7 +131,18 @@ public GroupedOpenApi getContestApi() { return GroupedOpenApi.builder() .group("공모전 게시판 관련") - .pathsToMatch("/contest/**", "/**/**/**/comment/**", "/**/**/**/comments") + .pathsToMatch( + "/contest/**", "/**/**/**/comment/**", "/**/**/**/comments", "/file/upload/**") + .build(); + } + + @Bean + public GroupedOpenApi getScholarshipApi() { + + return GroupedOpenApi.builder() + .group("장학회 관련") + .pathsToMatch( + "/scholarship/**", "/**/**/**/comment/**", "/**/**/**/comments", "/file/upload/**") .build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/config/WebConfig.java b/resource-server/src/main/java/com/inhabas/api/config/WebConfig.java index 2daa60e6..c73aae27 100644 --- a/resource-server/src/main/java/com/inhabas/api/config/WebConfig.java +++ b/resource-server/src/main/java/com/inhabas/api/config/WebConfig.java @@ -12,6 +12,7 @@ import com.inhabas.api.web.converter.MenuIdConverter; import com.inhabas.api.web.converter.NormalBoardTypeConverter; import com.inhabas.api.web.converter.ProjectBoardTypeConverter; +import com.inhabas.api.web.converter.ScholarshipBoardConverter; import io.swagger.v3.core.jackson.ModelResolver; @Configuration @@ -26,6 +27,8 @@ public void addFormatters(FormatterRegistry registry) { registry.addConverter(new ContestTypeConverter.ContestTypeToStringConverter()); registry.addConverter(new NormalBoardTypeConverter.NormalTypeToStringConverter()); registry.addConverter(new NormalBoardTypeConverter.StringToNormalTypeConverter()); + registry.addConverter(new ScholarshipBoardConverter.ScholarshipBoardTypeToStringConverter()); + registry.addConverter(new ScholarshipBoardConverter.StringToScholarshipTypeConverter()); registry.addConverter(new ProjectBoardTypeConverter.ProjectBoardTypeToStringConverter()); registry.addConverter(new ProjectBoardTypeConverter.StringToProjectBoardTypeConverter()); registry.addConverter(new MenuIdConverter.StringToMenuIdConverter()); diff --git a/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetApplicationRegisterForm.java b/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetApplicationRegisterForm.java index 24768d58..901b9cb8 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetApplicationRegisterForm.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetApplicationRegisterForm.java @@ -1,6 +1,8 @@ package com.inhabas.api.domain.budget.dto; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; @@ -37,14 +39,22 @@ public class BudgetApplicationRegisterForm { @NotBlank private String account; + private List files = new ArrayList<>(); + @Builder public BudgetApplicationRegisterForm( - String title, LocalDateTime dateUsed, String details, Integer outcome, String account) { + String title, + LocalDateTime dateUsed, + String details, + Integer outcome, + String account, + List files) { this.title = title; this.dateUsed = dateUsed; this.details = details; this.outcome = outcome; this.account = account; + this.files = files == null ? new ArrayList<>() : files; } public BudgetSupportApplication toEntity(Menu menu, Member applicant) { diff --git a/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetHistoryCreateForm.java b/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetHistoryCreateForm.java index 8264fd91..d627e41a 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetHistoryCreateForm.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/budget/dto/BudgetHistoryCreateForm.java @@ -1,6 +1,8 @@ package com.inhabas.api.domain.budget.dto; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; @@ -33,13 +35,15 @@ public class BudgetHistoryCreateForm { @NotBlank private String details; - @NotNull private String memberStudentIdReceived; + private String memberStudentIdReceived; - @NotBlank private String memberNameReceived; + private String memberNameReceived; - @PositiveOrZero @NotNull private Integer income; + @PositiveOrZero private Integer income; - @PositiveOrZero @NotNull private Integer outcome; + @PositiveOrZero private Integer outcome; + + @NotNull private List files = new ArrayList<>(); private static final Integer ZERO = 0; @@ -51,7 +55,8 @@ public BudgetHistoryCreateForm( String memberStudentIdReceived, String memberNameReceived, Integer income, - Integer outcome) { + Integer outcome, + List files) { this.dateUsed = dateUsed; this.title = title; this.details = details; @@ -59,7 +64,7 @@ public BudgetHistoryCreateForm( this.memberNameReceived = memberNameReceived; this.income = income; this.outcome = outcome; - + this.files = files == null ? new ArrayList<>() : files; if (this.details.isBlank()) { this.details = this.title; } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationService.java b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationService.java index 5c85821b..b7c2890e 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationService.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationService.java @@ -2,8 +2,6 @@ import java.util.List; -import org.springframework.web.multipart.MultipartFile; - import com.inhabas.api.auth.domain.oauth2.member.domain.valueObject.RequestStatus; import com.inhabas.api.domain.budget.dto.BudgetApplicationDetailDto; import com.inhabas.api.domain.budget.dto.BudgetApplicationDto; @@ -11,14 +9,9 @@ public interface BudgetApplicationService { - Long registerApplication( - BudgetApplicationRegisterForm form, List files, Long memberId); + Long registerApplication(BudgetApplicationRegisterForm form, Long memberId); - void updateApplication( - Long applicationId, - BudgetApplicationRegisterForm form, - List files, - Long memberId); + void updateApplication(Long applicationId, BudgetApplicationRegisterForm form, Long memberId); void deleteApplication(Long applicationId, Long memberId); diff --git a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationServiceImpl.java index d65c616b..65c79e82 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationServiceImpl.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetApplicationServiceImpl.java @@ -1,14 +1,11 @@ package com.inhabas.api.domain.budget.usecase; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; import com.inhabas.api.auth.domain.error.businessException.NotFoundException; import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; @@ -16,7 +13,6 @@ import com.inhabas.api.auth.domain.oauth2.member.domain.valueObject.RequestStatus; import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; import com.inhabas.api.domain.board.exception.OnlyWriterModifiableException; -import com.inhabas.api.domain.board.exception.S3UploadFailedException; import com.inhabas.api.domain.budget.domain.BudgetSupportApplication; import com.inhabas.api.domain.budget.dto.BudgetApplicationDetailDto; import com.inhabas.api.domain.budget.dto.BudgetApplicationDto; @@ -24,12 +20,11 @@ import com.inhabas.api.domain.budget.exception.InProcessModifiableException; import com.inhabas.api.domain.budget.repository.BudgetApplicationRepository; import com.inhabas.api.domain.file.domain.BoardFile; -import com.inhabas.api.domain.file.usecase.S3Service; +import com.inhabas.api.domain.file.repository.BoardFileRepository; import com.inhabas.api.domain.menu.domain.Menu; import com.inhabas.api.domain.menu.repository.MenuRepository; import com.inhabas.api.global.util.ClassifiedFiles; import com.inhabas.api.global.util.ClassifyFiles; -import com.inhabas.api.global.util.FileUtil; @Service @RequiredArgsConstructor @@ -38,7 +33,7 @@ public class BudgetApplicationServiceImpl implements BudgetApplicationService { private final BudgetApplicationRepository budgetApplicationRepository; private final MemberRepository memberRepository; private final MenuRepository menuRepository; - private final S3Service s3Service; + private final BoardFileRepository boardFileRepository; private static final Integer BUDGET_APPLICATION_MENU_ID = 14; private static final String DIR_NAME = "budget/"; @@ -76,8 +71,7 @@ public BudgetApplicationDetailDto getApplicationDetails(Long applicationId) { @Transactional @Override - public Long registerApplication( - BudgetApplicationRegisterForm form, List files, Long memberId) { + public Long registerApplication(BudgetApplicationRegisterForm form, Long memberId) { Member applicant = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); Menu menu = @@ -85,16 +79,22 @@ public Long registerApplication( BudgetSupportApplication application = form.toEntity(menu, applicant).writtenBy(applicant, BudgetSupportApplication.class); - return updateBudgetFiles(files, application); + List fileIdList = form.getFiles(); + List boardFileList = + boardFileRepository.getAllByIdInAndUploader(fileIdList, applicant); + application.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(application); + } + application.updateFiles(boardFileList); + return budgetApplicationRepository.save(application).getId(); } @Transactional @Override public void updateApplication( - Long applicationId, - BudgetApplicationRegisterForm form, - List files, - Long memberId) { + Long applicationId, BudgetApplicationRegisterForm form, Long memberId) { Member applicant = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); BudgetSupportApplication application = @@ -111,7 +111,14 @@ public void updateApplication( form.getOutcome(), form.getAccount()); - updateBudgetFiles(files, application); + List fileIdList = form.getFiles(); + List boardFileList = + boardFileRepository.getAllByIdInAndUploader(fileIdList, applicant); + application.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(application); + } } @Transactional @@ -126,37 +133,4 @@ public void deleteApplication(Long applicationId, Long memberId) { budgetApplicationRepository.deleteById(applicationId); } - - private Long updateBudgetFiles(List files, BudgetSupportApplication application) { - List updateReceipts = new ArrayList<>(); - List urlListForDelete = new ArrayList<>(); - - if (files != null) { - try { - updateReceipts = - files.stream() - .map( - file -> { - String path = FileUtil.generateFileName(file, DIR_NAME); - String url = s3Service.uploadS3Image(file, path); - urlListForDelete.add(url); - return BoardFile.builder() - .name(file.getOriginalFilename()) - .url(url) - .board(application) - .build(); - }) - .collect(Collectors.toList()); - - } catch (RuntimeException e) { - for (String url : urlListForDelete) { - s3Service.deleteS3File(url); - } - throw new S3UploadFailedException(); - } - } - - application.updateFiles(updateReceipts); - return budgetApplicationRepository.save(application).getId(); - } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryService.java b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryService.java index b9ac3d65..1f7cb620 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryService.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryService.java @@ -2,18 +2,15 @@ import java.util.List; -import org.springframework.web.multipart.MultipartFile; - import com.inhabas.api.domain.budget.dto.BudgetHistoryCreateForm; import com.inhabas.api.domain.budget.dto.BudgetHistoryDetailDto; import com.inhabas.api.domain.budget.dto.BudgetHistoryDto; public interface BudgetHistoryService { - Long createHistory(BudgetHistoryCreateForm form, List files, Long secretaryId); + Long createHistory(BudgetHistoryCreateForm form, Long secretaryId); - void modifyHistory( - Long historyId, BudgetHistoryCreateForm form, List files, Long secretaryId); + void modifyHistory(Long historyId, BudgetHistoryCreateForm form, Long secretaryId); void deleteHistory(Long historyId, Long secretaryId); diff --git a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryServiceImpl.java index 88432134..ad64eabb 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryServiceImpl.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/budget/usecase/BudgetHistoryServiceImpl.java @@ -1,33 +1,28 @@ package com.inhabas.api.domain.budget.usecase; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; import com.inhabas.api.auth.domain.error.businessException.NotFoundException; import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; import com.inhabas.api.domain.board.exception.OnlyWriterUpdateException; -import com.inhabas.api.domain.board.exception.S3UploadFailedException; import com.inhabas.api.domain.budget.domain.BudgetHistory; import com.inhabas.api.domain.budget.dto.BudgetHistoryCreateForm; import com.inhabas.api.domain.budget.dto.BudgetHistoryDetailDto; import com.inhabas.api.domain.budget.dto.BudgetHistoryDto; import com.inhabas.api.domain.budget.repository.BudgetHistoryRepository; import com.inhabas.api.domain.file.domain.BoardFile; -import com.inhabas.api.domain.file.usecase.S3Service; +import com.inhabas.api.domain.file.repository.BoardFileRepository; import com.inhabas.api.domain.menu.domain.Menu; import com.inhabas.api.domain.menu.repository.MenuRepository; import com.inhabas.api.global.util.ClassifiedFiles; import com.inhabas.api.global.util.ClassifyFiles; -import com.inhabas.api.global.util.FileUtil; @Service @RequiredArgsConstructor @@ -36,14 +31,12 @@ public class BudgetHistoryServiceImpl implements BudgetHistoryService { private final BudgetHistoryRepository budgetHistoryRepository; private final MemberRepository memberRepository; private final MenuRepository menuRepository; - private final S3Service s3Service; + private final BoardFileRepository boardFileRepository; private static final Integer BUDGET_HISTORY_MENU_ID = 15; - private static final String DIR_NAME = "budget/"; @Override @Transactional - public Long createHistory( - BudgetHistoryCreateForm form, List files, Long secretaryId) { + public Long createHistory(BudgetHistoryCreateForm form, Long secretaryId) { Member secretary = memberRepository.findById(secretaryId).orElseThrow(MemberNotFoundException::new); @@ -57,13 +50,20 @@ public Long createHistory( BudgetHistory newHistory = form.toEntity(menu, secretary, memberReceived).writtenBy(secretary, BudgetHistory.class); - return updateBudgetFiles(files, newHistory); + List fileIdList = form.getFiles(); + List boardFileList = + boardFileRepository.getAllByIdInAndUploader(fileIdList, secretary); + newHistory.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(newHistory); + } + return budgetHistoryRepository.save(newHistory).getId(); } @Override @Transactional - public void modifyHistory( - Long historyId, BudgetHistoryCreateForm form, List files, Long secretaryId) { + public void modifyHistory(Long historyId, BudgetHistoryCreateForm form, Long secretaryId) { Member secretary = memberRepository.findById(secretaryId).orElseThrow(MemberNotFoundException::new); Member memberReceived = @@ -84,7 +84,14 @@ public void modifyHistory( form.getDetails(), memberReceived); - updateBudgetFiles(files, budgetHistory); + List fileIdList = form.getFiles(); + List boardFileList = + boardFileRepository.getAllByIdInAndUploader(fileIdList, secretary); + budgetHistory.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(budgetHistory); + } } @Override @@ -147,37 +154,4 @@ public List getAllYearOfHistory() { public Integer getBalance() { return budgetHistoryRepository.getBalance(); } - - private Long updateBudgetFiles(List files, BudgetHistory budgetHistory) { - List updateFiles = new ArrayList<>(); - List urlListForDelete = new ArrayList<>(); - - if (files != null) { - try { - updateFiles = - files.stream() - .map( - file -> { - String path = FileUtil.generateFileName(file, DIR_NAME); - String url = s3Service.uploadS3Image(file, path); - urlListForDelete.add(url); - return BoardFile.builder() - .name(file.getOriginalFilename()) - .url(url) - .board(budgetHistory) - .build(); - }) - .collect(Collectors.toList()); - - } catch (RuntimeException e) { - for (String url : urlListForDelete) { - s3Service.deleteS3File(url); - } - throw new S3UploadFailedException(); - } - } - - budgetHistory.updateFiles(updateFiles); - return budgetHistoryRepository.save(budgetHistory).getId(); - } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/club/dto/SaveClubActivityDto.java b/resource-server/src/main/java/com/inhabas/api/domain/club/dto/SaveClubActivityDto.java index b84c845f..316d3ae2 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/club/dto/SaveClubActivityDto.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/club/dto/SaveClubActivityDto.java @@ -9,8 +9,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.web.multipart.MultipartFile; - @Getter @NoArgsConstructor public class SaveClubActivityDto { @@ -19,10 +17,10 @@ public class SaveClubActivityDto { @NotBlank private String content; - private List files; + private List files = new ArrayList<>(); @Builder - public SaveClubActivityDto(String title, String content, List files) { + public SaveClubActivityDto(String title, String content, List files) { this.title = title; this.content = content; this.files = (files != null) ? files : new ArrayList<>(); diff --git a/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityService.java b/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityService.java index 68e08344..38c804a6 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityService.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityService.java @@ -14,7 +14,7 @@ public interface ClubActivityService { ClubActivityDetailDto getClubActivity(Long boardId); - void updateClubActivity(Long boardId, SaveClubActivityDto saveClubActivityDto); + void updateClubActivity(Long boardId, SaveClubActivityDto saveClubActivityDto, Long memberId); void deleteClubActivity(Long boardId); } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityServiceImpl.java index 71dead33..f15ba3ed 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityServiceImpl.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/club/usecase/ClubActivityServiceImpl.java @@ -1,6 +1,5 @@ package com.inhabas.api.domain.club.usecase; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -14,18 +13,15 @@ import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; import com.inhabas.api.domain.board.domain.AlbumBoard; -import com.inhabas.api.domain.board.exception.S3UploadFailedException; -import com.inhabas.api.domain.board.usecase.BoardSecurityChecker; import com.inhabas.api.domain.club.dto.ClubActivityDetailDto; import com.inhabas.api.domain.club.dto.ClubActivityDto; import com.inhabas.api.domain.club.dto.SaveClubActivityDto; import com.inhabas.api.domain.club.repository.ClubActivityRepository; import com.inhabas.api.domain.file.domain.BoardFile; import com.inhabas.api.domain.file.dto.FileDownloadDto; -import com.inhabas.api.domain.file.usecase.S3Service; +import com.inhabas.api.domain.file.repository.BoardFileRepository; import com.inhabas.api.domain.menu.domain.Menu; import com.inhabas.api.domain.menu.repository.MenuRepository; -import com.inhabas.api.global.util.FileUtil; @Service @RequiredArgsConstructor @@ -33,24 +29,21 @@ public class ClubActivityServiceImpl implements ClubActivityService { private final ClubActivityRepository clubActivityRepository; - private final BoardSecurityChecker boardSecurityChecker; + private final BoardFileRepository boardFileRepository; private final MemberRepository memberRepository; private final MenuRepository menuRepository; - private final S3Service s3Service; - private static final String CLUB_ACTIVITY_MENU_NAME = "동아리 활동"; - private static final String DIR_NAME = "clubActivity/"; - @Override @Transactional(readOnly = true) public List getClubActivities() { List clubActivityList = clubActivityRepository.findAll(); + // 코드 수정 필요 return clubActivityList.stream() .map( obj -> { @@ -62,7 +55,13 @@ public List getClubActivities() { .writerName(obj.getWriter().getName()) .dateCreated(obj.getDateCreated()) .dateUpdated(obj.getDateUpdated()) - .thumbnail(new FileDownloadDto(fileName, fileUrl)) + .thumbnail( + new FileDownloadDto( + obj.getFiles().get(0).getId(), + fileName, + fileUrl, + obj.getFiles().get(0).getSize(), + obj.getFiles().get(0).getType())) .build(); }) .collect(Collectors.toList()); @@ -86,7 +85,15 @@ public Long writeClubActivity(Long memberId, SaveClubActivityDto saveClubActivit .build() .writtenBy(writer, AlbumBoard.class); - return updateClubActivityFiles(saveClubActivityDto, clubActivity); + List fileIdList = saveClubActivityDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + clubActivity.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(clubActivity); + } + + return clubActivityRepository.save(clubActivity).getId(); } @Override @@ -116,11 +123,19 @@ public ClubActivityDetailDto getClubActivity(Long boardId) { @Override @Transactional - public void updateClubActivity(Long boardId, SaveClubActivityDto saveClubActivityDto) { - + public void updateClubActivity( + Long boardId, SaveClubActivityDto saveClubActivityDto, Long memberId) { + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); AlbumBoard clubActivity = clubActivityRepository.findById(boardId).orElseThrow(NotFoundException::new); - updateClubActivityFiles(saveClubActivityDto, clubActivity); + + List fileIdList = saveClubActivityDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + clubActivity.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(clubActivity); + } } @Override @@ -129,39 +144,4 @@ public void deleteClubActivity(Long boardId) { clubActivityRepository.deleteById(boardId); } - - private Long updateClubActivityFiles( - SaveClubActivityDto saveClubActivityDto, AlbumBoard clubActivity) { - List updateFiles = new ArrayList<>(); - List urlListForDelete = new ArrayList<>(); - - if (saveClubActivityDto.getFiles() != null) { - clubActivity.updateText(saveClubActivityDto.getTitle(), saveClubActivityDto.getContent()); - try { - updateFiles = - saveClubActivityDto.getFiles().stream() - .map( - file -> { - String path = FileUtil.generateFileName(file, DIR_NAME); - String url = s3Service.uploadS3File(file, path); - urlListForDelete.add(url); - return BoardFile.builder() - .name(file.getOriginalFilename()) - .url(url) - .board(clubActivity) - .build(); - }) - .collect(Collectors.toList()); - - } catch (RuntimeException e) { - for (String url : urlListForDelete) { - s3Service.deleteS3File(url); - } - throw new S3UploadFailedException(); - } - } - - clubActivity.updateFiles(updateFiles); - return clubActivityRepository.save(clubActivity).getId(); - } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/contest/dto/SaveContestBoardDto.java b/resource-server/src/main/java/com/inhabas/api/domain/contest/dto/SaveContestBoardDto.java index 00b3ef45..1479b906 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/contest/dto/SaveContestBoardDto.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/contest/dto/SaveContestBoardDto.java @@ -1,6 +1,7 @@ package com.inhabas.api.domain.contest.dto; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import javax.validation.constraints.Future; @@ -11,8 +12,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.web.multipart.MultipartFile; - import org.hibernate.validator.constraints.Length; // 공모전 게시판 글 업데이트 및 저장 @@ -45,7 +44,7 @@ public class SaveContestBoardDto { @Future(message = "이미 모집기간이 종료된 공모전은 등록할 수 없습니다.") private LocalDate dateContestEnd; - private List files; + private List files = new ArrayList<>(); @Builder public SaveContestBoardDto( @@ -56,7 +55,7 @@ public SaveContestBoardDto( String topic, LocalDate dateContestStart, LocalDate dateContestEnd, - List files) { + List files) { this.contestFieldId = contestFieldId; this.title = title; @@ -65,6 +64,6 @@ public SaveContestBoardDto( this.topic = topic; this.dateContestStart = dateContestStart; this.dateContestEnd = dateContestEnd; - this.files = files; + this.files = files == null ? new ArrayList<>() : files; } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardService.java b/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardService.java index a8534582..4917e34c 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardService.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardService.java @@ -20,7 +20,10 @@ Long writeContestBoard( ContestBoardDetailDto getContestBoard(ContestType contestType, Long boardId); void updateContestBoard( - Long boardId, ContestType contestType, SaveContestBoardDto saveContestBoardDto); + Long boardId, + ContestType contestType, + SaveContestBoardDto saveContestBoardDto, + Long memberId); void deleteContestBoard(Long boardId); } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardServiceImpl.java index e807b6e4..c93b4299 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardServiceImpl.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/contest/usecase/ContestBoardServiceImpl.java @@ -4,7 +4,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -16,7 +15,6 @@ import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; -import com.inhabas.api.domain.board.exception.S3UploadFailedException; import com.inhabas.api.domain.contest.domain.ContestBoard; import com.inhabas.api.domain.contest.domain.ContestField; import com.inhabas.api.domain.contest.domain.ContestType; @@ -27,12 +25,11 @@ import com.inhabas.api.domain.contest.repository.ContestBoardRepository; import com.inhabas.api.domain.contest.repository.ContestFieldRepository; import com.inhabas.api.domain.file.domain.BoardFile; -import com.inhabas.api.domain.file.usecase.S3Service; +import com.inhabas.api.domain.file.repository.BoardFileRepository; import com.inhabas.api.domain.menu.domain.Menu; import com.inhabas.api.domain.menu.repository.MenuRepository; import com.inhabas.api.global.util.ClassifiedFiles; import com.inhabas.api.global.util.ClassifyFiles; -import com.inhabas.api.global.util.FileUtil; @Service @Slf4j @@ -43,7 +40,7 @@ public class ContestBoardServiceImpl implements ContestBoardService { private final ContestFieldRepository contestFieldRepository; private final MemberRepository memberRepository; private final MenuRepository menuRepository; - private final S3Service s3Service; + private final BoardFileRepository boardFileRepository; // 타입별 공모전 게시판 목록 조회 @Override @@ -121,14 +118,24 @@ public Long writeContestBoard( .build() .writtenBy(writer, ContestBoard.class); - return updateContestBoardFiles(saveContestBoardDto, contestType, contestBoard); + List fileIdList = saveContestBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + contestBoard.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(contestBoard); + } + return contestBoardRepository.save(contestBoard).getId(); } @Override @Transactional public void updateContestBoard( - Long boardId, ContestType contestType, SaveContestBoardDto saveContestBoardDto) { - + Long boardId, + ContestType contestType, + SaveContestBoardDto saveContestBoardDto, + Long memberId) { + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); ContestBoard contestBoard = contestBoardRepository.findById(boardId).orElseThrow(NotFoundException::new); ContestField contestField = @@ -143,7 +150,14 @@ public void updateContestBoard( saveContestBoardDto.getTopic(), saveContestBoardDto.getDateContestStart(), saveContestBoardDto.getDateContestEnd()); - updateContestBoardFiles(saveContestBoardDto, contestType, contestBoard); + + List fileIdList = saveContestBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + contestBoard.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(contestBoard); + } } @Override @@ -152,40 +166,4 @@ public void deleteContestBoard(Long boardId) { contestBoardRepository.deleteById(boardId); } - - private Long updateContestBoardFiles( - SaveContestBoardDto saveContestBoardDto, ContestType contestType, ContestBoard contestBoard) { - final String DIR_NAME = contestType.getBoardType() + "/"; - List updateFiles = new ArrayList<>(); - List urlListForDelete = new ArrayList<>(); - - if (saveContestBoardDto.getFiles() != null) { - - try { - updateFiles = - saveContestBoardDto.getFiles().stream() - .map( - file -> { - String path = FileUtil.generateFileName(file, DIR_NAME); - String url = s3Service.uploadS3File(file, path); - urlListForDelete.add(url); - return BoardFile.builder() - .name(file.getOriginalFilename()) - .url(url) - .board(contestBoard) - .build(); - }) - .collect(Collectors.toList()); - - } catch (RuntimeException e) { - for (String url : urlListForDelete) { - s3Service.deleteS3File(url); - } - throw new S3UploadFailedException(); - } - } - - contestBoard.updateFiles(updateFiles); - return contestBoardRepository.save(contestBoard).getId(); - } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BaseFile.java b/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BaseFile.java index 0717712b..c4bea7a8 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BaseFile.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BaseFile.java @@ -21,14 +21,16 @@ @EntityListeners(AuditingEntityListener.class) public class BaseFile { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - protected Long id; + @Id protected String id; @Embedded protected FileName name; @Embedded protected FileUrl url; + protected Long size; + + protected String type; + @CreatedDate @Column( nullable = false, @@ -37,9 +39,12 @@ public class BaseFile { columnDefinition = "DATETIME(0) DEFAULT CURRENT_TIMESTAMP") protected LocalDateTime dateCreated; - public BaseFile(String name, String url) { + public BaseFile(String id, String name, String url, Long size, String type) { + this.id = id; this.name = new FileName(name); this.url = new FileUrl(url); + this.size = size; + this.type = type; this.dateCreated = LocalDateTime.now(); } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BoardFile.java b/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BoardFile.java index 65c71823..083f4595 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BoardFile.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/file/domain/BoardFile.java @@ -11,6 +11,8 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import com.inhabas.api.auth.domain.error.businessException.InvalidInputException; +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; import com.inhabas.api.domain.board.domain.BaseBoard; @Entity @@ -20,24 +22,29 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class BoardFile extends BaseFile { - @ManyToOne(fetch = FetchType.LAZY, optional = false) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "BOARD_ID", foreignKey = @ForeignKey(name = "FK_FILE_OF_BOARD")) private BaseBoard board; - // boardFile 과 baseBoard 의 연관관계 편의 메소드 + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "USER_ID", foreignKey = @ForeignKey(name = "FK_MEMBER_OF_FILE")) + private Member uploader; @Builder - public BoardFile(String name, String url, BaseBoard board) { - super(name, url); - this.board = board; + public BoardFile(String id, String name, String url, Member uploader, Long size, String type) { + super(id, name, url, size, type); + this.uploader = uploader; + this.size = size; + this.type = type; } + // boardFile 과 baseBoard 의 연관관계 편의 메소드 public void toBoard(T newParentBoard) { - // 기존의 file-board 연관관계를 끊는다. - if (Objects.nonNull(this.board)) { - this.board.getFiles().remove(this); + if (Objects.isNull(this.board)) { + this.board = newParentBoard; + } else if (!this.board.getId().equals(newParentBoard.getId())) { + throw new InvalidInputException(); } - this.board = newParentBoard; } @Override diff --git a/resource-server/src/main/java/com/inhabas/api/domain/file/dto/FileDownloadDto.java b/resource-server/src/main/java/com/inhabas/api/domain/file/dto/FileDownloadDto.java index 7c5cbf7b..1e763695 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/file/dto/FileDownloadDto.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/file/dto/FileDownloadDto.java @@ -1,6 +1,7 @@ package com.inhabas.api.domain.file.dto; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; @@ -10,13 +11,18 @@ @NoArgsConstructor public class FileDownloadDto { + @NotNull private String id; @NotBlank private String name; - @NotBlank private String url; + @NotNull private Long size; + @NotNull private String type; @Builder - public FileDownloadDto(String name, String url) { + public FileDownloadDto(String id, String name, String url, Long size, String type) { + this.id = id; this.name = name; this.url = url; + this.size = size; + this.type = type; } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/file/repository/BoardFileRepository.java b/resource-server/src/main/java/com/inhabas/api/domain/file/repository/BoardFileRepository.java index 79fd365c..82da75ce 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/file/repository/BoardFileRepository.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/file/repository/BoardFileRepository.java @@ -1,7 +1,14 @@ package com.inhabas.api.domain.file.repository; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; import com.inhabas.api.domain.file.domain.BoardFile; -public interface BoardFileRepository extends JpaRepository {} +public interface BoardFileRepository extends JpaRepository { + List findAllByIdInAndUploader(List fileIdList, Member uploader); + + List getAllByIdInAndUploader(List fileIdList, Member uploader); +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/file/usecase/BoardFileService.java b/resource-server/src/main/java/com/inhabas/api/domain/file/usecase/BoardFileService.java new file mode 100644 index 00000000..8633d94b --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/file/usecase/BoardFileService.java @@ -0,0 +1,10 @@ +package com.inhabas.api.domain.file.usecase; + +import org.springframework.web.multipart.MultipartFile; + +import com.inhabas.api.domain.file.dto.FileDownloadDto; + +public interface BoardFileService { + + FileDownloadDto upload(Integer menuId, MultipartFile file, Long memberId); +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/file/usecase/BoardFileServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/file/usecase/BoardFileServiceImpl.java new file mode 100644 index 00000000..f91c6278 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/file/usecase/BoardFileServiceImpl.java @@ -0,0 +1,69 @@ +package com.inhabas.api.domain.file.usecase; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import com.inhabas.api.auth.domain.error.businessException.NotFoundException; +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; +import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; +import com.inhabas.api.domain.board.exception.S3UploadFailedException; +import com.inhabas.api.domain.file.domain.BoardFile; +import com.inhabas.api.domain.file.dto.FileDownloadDto; +import com.inhabas.api.domain.file.repository.BoardFileRepository; +import com.inhabas.api.domain.menu.domain.Menu; +import com.inhabas.api.domain.menu.repository.MenuRepository; +import com.inhabas.api.global.util.FileUtil; + +@Service +@Slf4j +@RequiredArgsConstructor +public class BoardFileServiceImpl implements BoardFileService { + + private final BoardFileRepository boardFileRepository; + private final MenuRepository menuRepository; + private final MemberRepository memberRepository; + private final S3Service s3Service; + + @Transactional + @Override + public FileDownloadDto upload(Integer menuId, MultipartFile file, Long memberId) { + + Member uploader = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); + Menu menu = menuRepository.findById(menuId).orElseThrow(NotFoundException::new); + BoardFile boardFile; + + try { + String id = FileUtil.generateUUID(); + String filePath = FileUtil.generateFilePathWithUUID(file, id, menu.getType().name()); + String url = s3Service.uploadS3File(file, filePath); + + boardFile = + BoardFile.builder() + .id(id) + .name(file.getOriginalFilename()) + .url(url) + .uploader(uploader) + .size(file.getSize()) + .type(file.getContentType()) + .build(); + + } catch (RuntimeException e) { + throw new S3UploadFailedException(); + } + + BoardFile savedBoardFile = boardFileRepository.save(boardFile); + + return FileDownloadDto.builder() + .id(savedBoardFile.getId()) + .name(savedBoardFile.getName()) + .url(savedBoardFile.getUrl()) + .size(savedBoardFile.getSize()) + .type(savedBoardFile.getType()) + .build(); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/menu/domain/valueObject/MenuType.java b/resource-server/src/main/java/com/inhabas/api/domain/menu/domain/valueObject/MenuType.java index 9e456e99..1d3b820c 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/menu/domain/valueObject/MenuType.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/menu/domain/valueObject/MenuType.java @@ -40,6 +40,10 @@ public enum MenuType { PROJECT(DEACTIVATED, BASIC, DEACTIVATED, DEACTIVATED, DEACTIVATED), // 공모전, 대외활동 CONTEST(ANONYMOUS, BASIC, ANONYMOUS, DEACTIVATED, BASIC), + // 후원 내용 + SPONSOR(EXECUTIVES, EXECUTIVES, EXECUTIVES, EXECUTIVES, EXECUTIVES), + // 사용 내역 + USAGE(EXECUTIVES, EXECUTIVES, EXECUTIVES, EXECUTIVES, EXECUTIVES), // 관리자가 추가, 삭제 가능한 메뉴.(NormalBoard 연관) LIST(ANONYMOUS, ANONYMOUS, ANONYMOUS, ANONYMOUS, ANONYMOUS), // 리스트형 게시판 메뉴 diff --git a/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/dto/SaveNormalBoardDto.java b/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/dto/SaveNormalBoardDto.java index 193bb21c..83106850 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/dto/SaveNormalBoardDto.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/dto/SaveNormalBoardDto.java @@ -1,5 +1,6 @@ package com.inhabas.api.domain.normalBoard.dto; +import java.util.ArrayList; import java.util.List; import javax.validation.constraints.NotBlank; @@ -8,8 +9,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.web.multipart.MultipartFile; - @Getter @NoArgsConstructor public class SaveNormalBoardDto { @@ -17,16 +16,15 @@ public class SaveNormalBoardDto { @NotBlank private String content; - private List files; + private List files = new ArrayList<>(); private Integer pinOption; @Builder - public SaveNormalBoardDto( - String title, String content, List files, Integer pinOption) { + public SaveNormalBoardDto(String title, String content, List files, Integer pinOption) { this.title = title; this.content = content; - this.files = files; + this.files = files == null ? new ArrayList<>() : files; this.pinOption = pinOption; } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardService.java b/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardService.java index 0f5acd35..6bfc7ce1 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardService.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardService.java @@ -17,7 +17,11 @@ public interface NormalBoardService { Long write(Long memberId, NormalBoardType normalBoardType, SaveNormalBoardDto saveNormalBoardDto); - void update(Long boardId, NormalBoardType normalBoardType, SaveNormalBoardDto saveNormalBoardDto); + void update( + Long boardId, + NormalBoardType boardType, + SaveNormalBoardDto saveNormalBoardDto, + Long memberId); void delete(Long boardId); } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardServiceImpl.java index a24c584c..7c898e62 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardServiceImpl.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/normalBoard/usecase/NormalBoardServiceImpl.java @@ -7,7 +7,6 @@ import java.time.LocalDateTime; import java.util.*; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -22,9 +21,8 @@ import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; -import com.inhabas.api.domain.board.exception.S3UploadFailedException; import com.inhabas.api.domain.file.domain.BoardFile; -import com.inhabas.api.domain.file.usecase.S3Service; +import com.inhabas.api.domain.file.repository.BoardFileRepository; import com.inhabas.api.domain.menu.domain.Menu; import com.inhabas.api.domain.menu.repository.MenuRepository; import com.inhabas.api.domain.normalBoard.domain.NormalBoard; @@ -35,7 +33,6 @@ import com.inhabas.api.domain.normalBoard.repository.NormalBoardRepository; import com.inhabas.api.global.util.ClassifiedFiles; import com.inhabas.api.global.util.ClassifyFiles; -import com.inhabas.api.global.util.FileUtil; @Service @Slf4j @@ -45,7 +42,7 @@ public class NormalBoardServiceImpl implements NormalBoardService { private final NormalBoardRepository normalBoardRepository; private final MenuRepository menuRepository; private final MemberRepository memberRepository; - private final S3Service s3Service; + private final BoardFileRepository boardFileRepository; private static final Set hasPinnedBoardTypeSet = new HashSet<>(Arrays.asList(NOTICE, EXECUTIVE)); @@ -126,19 +123,37 @@ public Long write( .writtenBy(writer, NormalBoard.class); updateNormalBoardPinned(saveNormalBoardDto, boardType, normalBoard); - return updateNormalBoardFiles(saveNormalBoardDto, boardType, normalBoard); + List fileIdList = saveNormalBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + normalBoard.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(normalBoard); + } + + return normalBoardRepository.save(normalBoard).getId(); } @Override @Transactional public void update( - Long boardId, NormalBoardType boardType, SaveNormalBoardDto saveNormalBoardDto) { - + Long boardId, + NormalBoardType boardType, + SaveNormalBoardDto saveNormalBoardDto, + Long memberId) { + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); NormalBoard normalBoard = normalBoardRepository.findById(boardId).orElseThrow(NotFoundException::new); normalBoard.updateText(saveNormalBoardDto.getTitle(), saveNormalBoardDto.getContent()); updateNormalBoardPinned(saveNormalBoardDto, boardType, normalBoard); - updateNormalBoardFiles(saveNormalBoardDto, boardType, normalBoard); + + List fileIdList = saveNormalBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + normalBoard.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(normalBoard); + } } @Override @@ -147,41 +162,6 @@ public void delete(Long boardId) { normalBoardRepository.deleteById(boardId); } - private Long updateNormalBoardFiles( - SaveNormalBoardDto saveNormalBoardDto, NormalBoardType boardType, NormalBoard normalBoard) { - final String DIR_NAME = boardType.getBoardType() + "/"; - List updateFiles = new ArrayList<>(); - List urlListForDelete = new ArrayList<>(); - - if (saveNormalBoardDto.getFiles() != null) { - try { - updateFiles = - saveNormalBoardDto.getFiles().stream() - .map( - file -> { - String path = FileUtil.generateFileName(file, DIR_NAME); - String url = s3Service.uploadS3File(file, path); - urlListForDelete.add(url); - return BoardFile.builder() - .name(file.getOriginalFilename()) - .url(url) - .board(normalBoard) - .build(); - }) - .collect(Collectors.toList()); - - } catch (RuntimeException e) { - for (String url : urlListForDelete) { - s3Service.deleteS3File(url); - } - throw new S3UploadFailedException(); - } - } - - normalBoard.updateFiles(updateFiles); - return normalBoardRepository.save(normalBoard).getId(); - } - private void updateNormalBoardPinned( SaveNormalBoardDto saveNormalBoardDto, NormalBoardType boardType, NormalBoard normalBoard) { boolean isPinned = false; diff --git a/resource-server/src/main/java/com/inhabas/api/domain/project/dto/SaveProjectBoardDto.java b/resource-server/src/main/java/com/inhabas/api/domain/project/dto/SaveProjectBoardDto.java index e7d57052..92d1dbf8 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/project/dto/SaveProjectBoardDto.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/project/dto/SaveProjectBoardDto.java @@ -1,5 +1,6 @@ package com.inhabas.api.domain.project.dto; +import java.util.ArrayList; import java.util.List; import javax.validation.constraints.NotBlank; @@ -8,8 +9,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.web.multipart.MultipartFile; - @Getter @NoArgsConstructor public class SaveProjectBoardDto { @@ -18,16 +17,15 @@ public class SaveProjectBoardDto { @NotBlank private String content; - private List files; + private List files = new ArrayList<>(); private Integer pinOption; @Builder - public SaveProjectBoardDto( - String title, String content, List files, Integer pinOption) { + public SaveProjectBoardDto(String title, String content, List files, Integer pinOption) { this.title = title; this.content = content; - this.files = files; + this.files = files == null ? new ArrayList<>() : files; this.pinOption = pinOption; } } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardService.java b/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardService.java index 5a604418..a0808d32 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardService.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardService.java @@ -19,7 +19,10 @@ Long write( Long memberId, ProjectBoardType projectBoardType, SaveProjectBoardDto saveProjectBoardDto); void update( - Long boardId, ProjectBoardType projectBoardType, SaveProjectBoardDto saveProjectBoardDto); + Long boardId, + ProjectBoardType projectBoardType, + SaveProjectBoardDto saveProjectBoardDto, + Long memberId); void delete(Long boardId); } diff --git a/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardServiceImpl.java index d24feed5..81c7baec 100644 --- a/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardServiceImpl.java +++ b/resource-server/src/main/java/com/inhabas/api/domain/project/usecase/ProjectBoardServiceImpl.java @@ -11,7 +11,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -24,9 +23,8 @@ import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; -import com.inhabas.api.domain.board.exception.S3UploadFailedException; import com.inhabas.api.domain.file.domain.BoardFile; -import com.inhabas.api.domain.file.usecase.S3Service; +import com.inhabas.api.domain.file.repository.BoardFileRepository; import com.inhabas.api.domain.menu.domain.Menu; import com.inhabas.api.domain.menu.repository.MenuRepository; import com.inhabas.api.domain.project.domain.ProjectBoard; @@ -37,7 +35,6 @@ import com.inhabas.api.domain.project.repository.ProjectBoardRepository; import com.inhabas.api.global.util.ClassifiedFiles; import com.inhabas.api.global.util.ClassifyFiles; -import com.inhabas.api.global.util.FileUtil; @Service @Slf4j @@ -47,7 +44,7 @@ public class ProjectBoardServiceImpl implements ProjectBoardService { private final ProjectBoardRepository projectBoardRepository; private final MenuRepository menuRepository; private final MemberRepository memberRepository; - private final S3Service s3Service; + private final BoardFileRepository boardFileRepository; private static final Set hasPinnedBoardTypeSet = new HashSet<>(Arrays.asList(ALPHA, BETA)); @@ -115,19 +112,38 @@ public Long write( .writtenBy(writer, ProjectBoard.class); updateProjectBoardPinned(saveProjectBoardDto, projectBoardType, projectBoard); - return updateProjectBoardFiles(saveProjectBoardDto, projectBoardType, projectBoard); + + List fileIdList = saveProjectBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + projectBoard.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(projectBoard); + } + + return projectBoardRepository.save(projectBoard).getId(); } @Override @Transactional public void update( - Long boardId, ProjectBoardType projectBoardType, SaveProjectBoardDto saveProjectBoardDto) { - + Long boardId, + ProjectBoardType projectBoardType, + SaveProjectBoardDto saveProjectBoardDto, + Long memberId) { + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); ProjectBoard projectBoard = projectBoardRepository.findById(boardId).orElseThrow(NotFoundException::new); projectBoard.updateText(saveProjectBoardDto.getTitle(), saveProjectBoardDto.getContent()); updateProjectBoardPinned(saveProjectBoardDto, projectBoardType, projectBoard); - updateProjectBoardFiles(saveProjectBoardDto, projectBoardType, projectBoard); + + List fileIdList = saveProjectBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + projectBoard.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(projectBoard); + } } @Override @@ -136,43 +152,6 @@ public void delete(Long boardId) { projectBoardRepository.deleteById(boardId); } - private Long updateProjectBoardFiles( - SaveProjectBoardDto saveProjectBoardDto, - ProjectBoardType projectBoardType, - ProjectBoard projectBoard) { - final String DIR_NAME = projectBoardType.getBoardType() + "/"; - List updateFiles = new ArrayList<>(); - List urlListForDelete = new ArrayList<>(); - - if (saveProjectBoardDto.getFiles() != null) { - try { - updateFiles = - saveProjectBoardDto.getFiles().stream() - .map( - file -> { - String path = FileUtil.generateFileName(file, DIR_NAME); - String url = s3Service.uploadS3File(file, path); - urlListForDelete.add(url); - return BoardFile.builder() - .name(file.getOriginalFilename()) - .url(url) - .board(projectBoard) - .build(); - }) - .collect(Collectors.toList()); - - } catch (RuntimeException e) { - for (String url : urlListForDelete) { - s3Service.deleteS3File(url); - } - throw new S3UploadFailedException(); - } - } - - projectBoard.updateFiles(updateFiles); - return projectBoardRepository.save(projectBoard).getId(); - } - private void updateProjectBoardPinned( SaveProjectBoardDto saveProjectBoardDto, ProjectBoardType projectBoardType, diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/Scholarship.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/Scholarship.java new file mode 100644 index 00000000..0413f19d --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/Scholarship.java @@ -0,0 +1,77 @@ +package com.inhabas.api.domain.scholarship.domain; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.Table; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import com.inhabas.api.domain.board.domain.BaseBoard; +import com.inhabas.api.domain.board.domain.valueObject.Content; +import com.inhabas.api.domain.board.domain.valueObject.Title; +import com.inhabas.api.domain.file.domain.BoardFile; +import com.inhabas.api.domain.menu.domain.Menu; + +@Entity +@Table(name = "SCHOLARSHIP_BOARD") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EntityListeners(AuditingEntityListener.class) +@Inheritance(strategy = InheritanceType.JOINED) +@DiscriminatorValue("SCHOLARSHIP") +public class Scholarship extends BaseBoard { + + @Embedded private Content content; + + @Column(name = "DATE_HISTORY", nullable = false, columnDefinition = "DATETIME(0)") + private LocalDateTime dateHistory; + + /* constructor */ + + public Scholarship(String title, Menu menu, String content, LocalDateTime dateHistory) { + super(title, menu); + this.content = new Content(content); + this.dateHistory = dateHistory; + } + + /* getter */ + + public String getContent() { + return content.getValue(); + } + + public List getFiles() { + return Collections.unmodifiableList(files); + } + + public void updateText(String title, String content, LocalDateTime dateHistory) { + this.title = new Title(title); + this.content = new Content(content); + this.dateHistory = dateHistory; + } + + public void updateFiles(List files) { + + if (this.files != null) { + this.files.clear(); + } else { + this.files = new ArrayList<>(); + } + + for (BoardFile file : files) { + addFile(file); + } + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/ScholarshipBoardType.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/ScholarshipBoardType.java new file mode 100644 index 00000000..9c35bc35 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/ScholarshipBoardType.java @@ -0,0 +1,22 @@ +package com.inhabas.api.domain.scholarship.domain; + +public enum ScholarshipBoardType { + SPONSOR("sponsor", 20), + USAGE("usage", 21); + + private final String boardType; + private final int menuId; + + ScholarshipBoardType(String boardType, int menuId) { + this.boardType = boardType; + this.menuId = menuId; + } + + public String getBoardType() { + return boardType; + } + + public int getMenuId() { + return menuId; + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/ScholarshipHistory.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/ScholarshipHistory.java new file mode 100644 index 00000000..e8e2978c --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/domain/ScholarshipHistory.java @@ -0,0 +1,66 @@ +package com.inhabas.api.domain.scholarship.domain; + +import java.time.LocalDateTime; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.domain.BaseEntity; +import com.inhabas.api.domain.board.domain.valueObject.Title; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipHistoryDto; + +@Entity +@Getter +@Table(name = "SCHOLARSHIP_HISTORY") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EntityListeners(AuditingEntityListener.class) +public class ScholarshipHistory extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "USER_ID", foreignKey = @ForeignKey(name = "FK_MEMBER_OF_SCHOLARSHIP_HISTORY")) + private Member writer; + + @Embedded private Title title; + + @Column(name = "DATE_HISTORY", nullable = false, columnDefinition = "DATETIME(0)") + private LocalDateTime dateHistory; + + @Builder + public ScholarshipHistory(Member writer, String title, LocalDateTime dateHistory) { + this.writer = writer; + this.title = new Title(title); + this.dateHistory = dateHistory; + } + + public String getTitle() { + return title.getValue(); + } + + public void update(Member writer, SaveScholarshipHistoryDto saveScholarshipHistoryDto) { + this.writer = writer; + this.title = new Title(saveScholarshipHistoryDto.getTitle()); + this.dateHistory = saveScholarshipHistoryDto.getDateHistory(); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipBoardDto.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipBoardDto.java new file mode 100644 index 00000000..26e1580d --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipBoardDto.java @@ -0,0 +1,42 @@ +package com.inhabas.api.domain.scholarship.dto; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Past; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; + +@Getter +@NoArgsConstructor +public class SaveScholarshipBoardDto { + + @NotBlank private String title; + + @NotBlank private String content; + + @NotNull + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + @Schema(type = "string", example = "2024-11-01T00:00:00") + @Past + private LocalDateTime dateHistory; + + private List files = new ArrayList<>(); + + @Builder + public SaveScholarshipBoardDto( + String title, String content, LocalDateTime dateHistory, List files) { + this.title = title; + this.content = content; + this.dateHistory = dateHistory; + this.files = files == null ? new ArrayList<>() : files; + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipHistoryDto.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipHistoryDto.java new file mode 100644 index 00000000..b95f9d83 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipHistoryDto.java @@ -0,0 +1,33 @@ +package com.inhabas.api.domain.scholarship.dto; + +import java.time.LocalDateTime; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Past; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; + +@Getter +@NoArgsConstructor +public class SaveScholarshipHistoryDto { + + @NotBlank String title; + + @NotNull + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + @Schema(type = "string", example = "2024-11-01T00:00:00") + @Past + LocalDateTime dateHistory; + + @Builder + public SaveScholarshipHistoryDto(String title, LocalDateTime dateHistory) { + this.title = title; + this.dateHistory = dateHistory; + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/ScholarshipBoardDetailDto.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/ScholarshipBoardDetailDto.java new file mode 100644 index 00000000..8519fdf6 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/ScholarshipBoardDetailDto.java @@ -0,0 +1,74 @@ +package com.inhabas.api.domain.scholarship.dto; + +import java.time.LocalDateTime; +import java.util.List; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Positive; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.domain.file.dto.FileDownloadDto; +import io.swagger.v3.oas.annotations.media.Schema; + +@Getter +@NoArgsConstructor +public class ScholarshipBoardDetailDto { + + @NotNull @Positive private Long id; + + @NotBlank private String title; + + @NotBlank private String content; + + @NotNull private Long writerId; + + @NotBlank private String writerName; + + @NotNull + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + @Schema(type = "string", example = "2024-11-01T00:00:00") + private LocalDateTime dateHistory; + + @NotNull + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + @Schema(type = "string", example = "2024-11-01T00:00:00") + private LocalDateTime dateCreated; + + @NotNull + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + @Schema(type = "string", example = "2024-11-01T00:00:00") + private LocalDateTime dateUpdated; + + @NotNull private List images; + + @NotNull private List otherFiles; + + @Builder + public ScholarshipBoardDetailDto( + Long id, + String title, + String content, + Member writer, + LocalDateTime dateHistory, + LocalDateTime dateCreated, + LocalDateTime dateUpdated, + List images, + List otherFiles) { + this.id = id; + this.title = title; + this.content = content; + this.writerId = writer.getId(); + this.writerName = writer.getName(); + this.dateHistory = dateHistory; + this.dateCreated = dateCreated; + this.dateUpdated = dateUpdated; + this.images = images; + this.otherFiles = otherFiles; + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/ScholarshipBoardDto.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/ScholarshipBoardDto.java new file mode 100644 index 00000000..ae0472f4 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/dto/ScholarshipBoardDto.java @@ -0,0 +1,49 @@ +package com.inhabas.api.domain.scholarship.dto; + +import java.time.LocalDateTime; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Positive; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import io.swagger.v3.oas.annotations.media.Schema; + +@Getter +@NoArgsConstructor +public class ScholarshipBoardDto { + + @NotNull @Positive private Long id; + + @NotBlank private String title; + + @NotNull private Long writerId; + + @NotBlank private String writerName; + + @NotNull + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + @Schema(type = "string", example = "2024-11-01T00:00:00") + private LocalDateTime dateCreated; + + @NotNull + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + @Schema(type = "string", example = "2024-11-01T00:00:00") + private LocalDateTime dateUpdated; + + @Builder + public ScholarshipBoardDto( + Long id, String title, Member writer, LocalDateTime dateCreated, LocalDateTime dateUpdated) { + this.id = id; + this.title = title; + this.writerId = writer.getId(); + this.writerName = writer.getName(); + this.dateCreated = dateCreated; + this.dateUpdated = dateUpdated; + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepository.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepository.java new file mode 100644 index 00000000..a43c6f19 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepository.java @@ -0,0 +1,8 @@ +package com.inhabas.api.domain.scholarship.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.inhabas.api.domain.scholarship.domain.Scholarship; + +public interface ScholarshipBoardRepository + extends JpaRepository, ScholarshipBoardRepositoryCustom {} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepositoryCustom.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepositoryCustom.java new file mode 100644 index 00000000..6713a5fb --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepositoryCustom.java @@ -0,0 +1,15 @@ +package com.inhabas.api.domain.scholarship.repository; + +import java.util.List; +import java.util.Optional; + +import com.inhabas.api.domain.scholarship.domain.Scholarship; +import com.inhabas.api.domain.scholarship.domain.ScholarshipBoardType; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDto; + +public interface ScholarshipBoardRepositoryCustom { + + List findAllByTypeAndSearch(ScholarshipBoardType boardType, String search); + + Optional findByTypeAndId(ScholarshipBoardType boardType, Long boardId); +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepositoryImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepositoryImpl.java new file mode 100644 index 00000000..3ea7c28d --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipBoardRepositoryImpl.java @@ -0,0 +1,53 @@ +package com.inhabas.api.domain.scholarship.repository; + +import static com.inhabas.api.domain.scholarship.domain.QScholarship.scholarship; + +import java.util.List; +import java.util.Optional; + +import lombok.RequiredArgsConstructor; + +import com.inhabas.api.domain.scholarship.domain.Scholarship; +import com.inhabas.api.domain.scholarship.domain.ScholarshipBoardType; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDto; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; + +@RequiredArgsConstructor +public class ScholarshipBoardRepositoryImpl implements ScholarshipBoardRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public List findAllByTypeAndSearch( + ScholarshipBoardType boardType, String search) { + return queryFactory + .select( + Projections.constructor( + ScholarshipBoardDto.class, + scholarship.id, + scholarship.title.value, + scholarship.writer, + scholarship.dateCreated, + scholarship.dateUpdated)) + .from(scholarship) + .where(eqScholarshipBoardType(boardType)) + .orderBy(scholarship.dateCreated.desc()) + .fetch(); + } + + @Override + public Optional findByTypeAndId(ScholarshipBoardType boardType, Long boardId) { + return Optional.ofNullable( + queryFactory + .selectFrom(scholarship) + .where((eqScholarshipBoardType(boardType)).and(scholarship.id.eq(boardId))) + .orderBy(scholarship.dateCreated.desc()) + .fetchOne()); + } + + private BooleanExpression eqScholarshipBoardType(ScholarshipBoardType scholarshipBoardType) { + return scholarship.menu.id.eq(scholarshipBoardType.getMenuId()); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepository.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepository.java new file mode 100644 index 00000000..4198870f --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepository.java @@ -0,0 +1,8 @@ +package com.inhabas.api.domain.scholarship.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.inhabas.api.domain.scholarship.domain.ScholarshipHistory; + +public interface ScholarshipHistoryRepository + extends JpaRepository, ScholarshipHistoryRepositoryCustom {} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryCustom.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryCustom.java new file mode 100644 index 00000000..fc719cc8 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryCustom.java @@ -0,0 +1,9 @@ +package com.inhabas.api.domain.scholarship.repository; + +import java.util.List; + +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.YearlyData; + +public interface ScholarshipHistoryRepositoryCustom { + List getYearlyData(); +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryImpl.java new file mode 100644 index 00000000..41172bf9 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryImpl.java @@ -0,0 +1,70 @@ +package com.inhabas.api.domain.scholarship.repository; + +import static com.inhabas.api.domain.scholarship.domain.QScholarshipHistory.scholarshipHistory; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.inhabas.api.domain.scholarship.domain.ScholarshipHistory; +import com.querydsl.jpa.impl.JPAQueryFactory; + +@RequiredArgsConstructor +public class ScholarshipHistoryRepositoryImpl implements ScholarshipHistoryRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public List getYearlyData() { + + List histories = + queryFactory + .selectFrom(scholarshipHistory) + .orderBy( + scholarshipHistory.dateHistory.year().asc(), scholarshipHistory.dateHistory.asc()) + .fetch(); + + return histories.stream() + .collect( + Collectors.groupingBy( + history -> history.getDateHistory().getYear(), + Collectors.mapping( + history -> + new Data(history.getId(), history.getTitle(), history.getDateHistory()), + Collectors.toList()))) + .entrySet() + .stream() + .map(entry -> new YearlyData(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + // 연도별 컨텐츠를 담는 클래스 + public static class YearlyData { + + public int year; + public List data; + + public YearlyData(int year, List data) { + this.year = year; + this.data = data; + } + } + + public static class Data { + + public Long id; + public String title; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + public LocalDateTime dateHistory; + + public Data(Long id, String title, LocalDateTime dateHistory) { + this.id = id; + this.title = title; + this.dateHistory = dateHistory; + } + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipBoardService.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipBoardService.java new file mode 100644 index 00000000..c2964008 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipBoardService.java @@ -0,0 +1,28 @@ +package com.inhabas.api.domain.scholarship.usecase; + +import java.util.List; + +import com.inhabas.api.domain.scholarship.domain.ScholarshipBoardType; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipBoardDto; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDetailDto; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDto; + +public interface ScholarshipBoardService { + + List getPosts(ScholarshipBoardType boardType, String search); + + ScholarshipBoardDetailDto getPost(ScholarshipBoardType boardType, Long boardId, Long memberId); + + Long write( + ScholarshipBoardType boardType, + SaveScholarshipBoardDto saveScholarshipBoardDto, + Long memberId); + + void update( + Long boardId, + ScholarshipBoardType boardType, + SaveScholarshipBoardDto saveScholarshipBoardDto, + Long memberId); + + void delete(ScholarshipBoardType boardType, Long boardId); +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipBoardServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipBoardServiceImpl.java new file mode 100644 index 00000000..7a8516ef --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipBoardServiceImpl.java @@ -0,0 +1,139 @@ +package com.inhabas.api.domain.scholarship.usecase; + +import java.util.ArrayList; +import java.util.List; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.inhabas.api.auth.domain.error.businessException.NotFoundException; +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; +import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; +import com.inhabas.api.domain.file.domain.BoardFile; +import com.inhabas.api.domain.file.repository.BoardFileRepository; +import com.inhabas.api.domain.menu.domain.Menu; +import com.inhabas.api.domain.menu.repository.MenuRepository; +import com.inhabas.api.domain.scholarship.domain.Scholarship; +import com.inhabas.api.domain.scholarship.domain.ScholarshipBoardType; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipBoardDto; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDetailDto; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDto; +import com.inhabas.api.domain.scholarship.repository.ScholarshipBoardRepository; +import com.inhabas.api.global.util.ClassifiedFiles; +import com.inhabas.api.global.util.ClassifyFiles; + +@Service +@Slf4j +@javax.transaction.Transactional +@RequiredArgsConstructor +public class ScholarshipBoardServiceImpl implements ScholarshipBoardService { + + private final BoardFileRepository boardFileRepository; + + private final ScholarshipBoardRepository scholarshipBoardRepository; + private final MenuRepository menuRepository; + private final MemberRepository memberRepository; + + @Transactional(readOnly = true) + @Override + public List getPosts(ScholarshipBoardType boardType, String search) { + List scholarshipBoardDtoList = new ArrayList<>(); + scholarshipBoardDtoList.addAll( + scholarshipBoardRepository.findAllByTypeAndSearch(boardType, search)); + return scholarshipBoardDtoList; + } + + @Transactional(readOnly = true) + @Override + public ScholarshipBoardDetailDto getPost( + ScholarshipBoardType boardType, Long boardId, Long memberId) { + + Scholarship scholarship = + scholarshipBoardRepository + .findByTypeAndId(boardType, boardId) + .orElseThrow(NotFoundException::new); + + ClassifiedFiles classifiedFiles = ClassifyFiles.classifyFiles(scholarship.getFiles()); + + return ScholarshipBoardDetailDto.builder() + .id(scholarship.getId()) + .title(scholarship.getTitle()) + .content(scholarship.getContent()) + .writer(scholarship.getWriter()) + .dateCreated(scholarship.getDateCreated()) + .dateUpdated(scholarship.getDateUpdated()) + .images(classifiedFiles.getImages()) + .otherFiles(classifiedFiles.getOtherFiles()) + .build(); + } + + @Transactional + @Override + public Long write( + ScholarshipBoardType boardType, + SaveScholarshipBoardDto saveScholarshipBoardDto, + Long memberId) { + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); + Menu menu = menuRepository.findById(boardType.getMenuId()).orElseThrow(NotFoundException::new); + + Scholarship scholarship = + new Scholarship( + saveScholarshipBoardDto.getTitle(), + menu, + saveScholarshipBoardDto.getContent(), + saveScholarshipBoardDto.getDateHistory()) + .writtenBy(writer, Scholarship.class); + + List fileIdList = saveScholarshipBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + scholarship.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(scholarship); + } + + return scholarshipBoardRepository.save(scholarship).getId(); + } + + @Transactional + @Override + public void update( + Long boardId, + ScholarshipBoardType boardType, + SaveScholarshipBoardDto saveScholarshipBoardDto, + Long memberId) { + Scholarship scholarship = + scholarshipBoardRepository + .findByTypeAndId(boardType, boardId) + .orElseThrow(NotFoundException::new); + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); + + scholarship.updateText( + saveScholarshipBoardDto.getTitle(), + saveScholarshipBoardDto.getContent(), + saveScholarshipBoardDto.getDateHistory()); + + List fileIdList = saveScholarshipBoardDto.getFiles(); + List boardFileList = boardFileRepository.getAllByIdInAndUploader(fileIdList, writer); + scholarship.updateFiles(boardFileList); + + for (BoardFile file : boardFileList) { + file.toBoard(scholarship); + } + } + + @Transactional + @Override + public void delete(ScholarshipBoardType boardType, Long boardId) { + Scholarship scholarship = + scholarshipBoardRepository + .findByTypeAndId(boardType, boardId) + .orElseThrow(NotFoundException::new); + + scholarshipBoardRepository.delete(scholarship); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryService.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryService.java new file mode 100644 index 00000000..e7cde1f9 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryService.java @@ -0,0 +1,20 @@ +package com.inhabas.api.domain.scholarship.usecase; + +import java.util.List; + +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipHistoryDto; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.YearlyData; + +public interface ScholarshipHistoryService { + + List getScholarshipHistories(); + + Long writeScholarshipHistory(Long memberId, SaveScholarshipHistoryDto saveScholarshipHistoryDto); + + void updateScholarshipHistory( + Long memberId, + Long scholarshipHistoryId, + SaveScholarshipHistoryDto saveScholarshipHistoryDto); + + void deleteScholarshipHistory(Long scholarshipHistoryId); +} diff --git a/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryServiceImpl.java b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryServiceImpl.java new file mode 100644 index 00000000..e1241de4 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryServiceImpl.java @@ -0,0 +1,72 @@ +package com.inhabas.api.domain.scholarship.usecase; + +import java.util.List; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.inhabas.api.auth.domain.error.businessException.NotFoundException; +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.auth.domain.oauth2.member.domain.exception.MemberNotFoundException; +import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; +import com.inhabas.api.domain.scholarship.domain.ScholarshipHistory; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipHistoryDto; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepository; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.YearlyData; + +@Service +@RequiredArgsConstructor +public class ScholarshipHistoryServiceImpl implements ScholarshipHistoryService { + + private final ScholarshipHistoryRepository scholarshipHistoryRepository; + private final MemberRepository memberRepository; + + @Transactional(readOnly = true) + @Override + public List getScholarshipHistories() { + + return scholarshipHistoryRepository.getYearlyData(); + } + + @Transactional + @Override + public Long writeScholarshipHistory( + Long memberId, SaveScholarshipHistoryDto saveScholarshipHistoryDto) { + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); + ScholarshipHistory scholarshipHistory = + ScholarshipHistory.builder() + .writer(writer) + .title(saveScholarshipHistoryDto.getTitle()) + .dateHistory(saveScholarshipHistoryDto.getDateHistory()) + .build(); + + return scholarshipHistoryRepository.save(scholarshipHistory).getId(); + } + + @Transactional + @Override + public void updateScholarshipHistory( + Long memberId, + Long scholarshipHistoryId, + SaveScholarshipHistoryDto saveScholarshipHistoryDto) { + ScholarshipHistory scholarshipHistory = + scholarshipHistoryRepository + .findById(scholarshipHistoryId) + .orElseThrow(NotFoundException::new); + Member writer = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); + + scholarshipHistory.update(writer, saveScholarshipHistoryDto); + } + + @Transactional + @Override + public void deleteScholarshipHistory(Long scholarshipHistoryId) { + ScholarshipHistory scholarshipHistory = + scholarshipHistoryRepository + .findById(scholarshipHistoryId) + .orElseThrow(NotFoundException::new); + scholarshipHistoryRepository.delete(scholarshipHistory); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/global/util/ClassifyFiles.java b/resource-server/src/main/java/com/inhabas/api/global/util/ClassifyFiles.java index 6da6dd77..bca52f30 100644 --- a/resource-server/src/main/java/com/inhabas/api/global/util/ClassifyFiles.java +++ b/resource-server/src/main/java/com/inhabas/api/global/util/ClassifyFiles.java @@ -12,7 +12,9 @@ public static ClassifiedFiles classifyFiles(List files) { ClassifiedFiles result = new ClassifiedFiles(); for (BaseFile file : files) { - FileDownloadDto fileDto = new FileDownloadDto(file.getName(), file.getUrl()); + FileDownloadDto fileDto = + new FileDownloadDto( + file.getId(), file.getName(), file.getUrl(), file.getSize(), file.getType()); if (FileUtil.isImageFile(file.getName())) { result.addImage(fileDto); if (result.getThumbnail() == null) { diff --git a/resource-server/src/main/java/com/inhabas/api/global/util/FileUtil.java b/resource-server/src/main/java/com/inhabas/api/global/util/FileUtil.java index 0f379b08..008580c7 100644 --- a/resource-server/src/main/java/com/inhabas/api/global/util/FileUtil.java +++ b/resource-server/src/main/java/com/inhabas/api/global/util/FileUtil.java @@ -74,7 +74,16 @@ public static String getNameWithoutExtension(String fileName) { return fileName.substring(0, fileName.lastIndexOf(".")); } + public static String generateUUID() { + return UUID.randomUUID().toString(); + } + public static String generateFileName(MultipartFile multipartFile, String dirName) { return dirName + UUID.randomUUID() + "_" + multipartFile.getOriginalFilename(); } + + public static String generateFilePathWithUUID( + MultipartFile multipartFile, String UUID, String dirName) { + return dirName + UUID + "_" + multipartFile.getOriginalFilename(); + } } diff --git a/resource-server/src/main/java/com/inhabas/api/web/BudgetApplicationController.java b/resource-server/src/main/java/com/inhabas/api/web/BudgetApplicationController.java index 33f129e1..b68a8ca3 100644 --- a/resource-server/src/main/java/com/inhabas/api/web/BudgetApplicationController.java +++ b/resource-server/src/main/java/com/inhabas/api/web/BudgetApplicationController.java @@ -20,9 +20,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.inhabas.api.auth.domain.error.ErrorResponse; @@ -104,7 +102,7 @@ public ResponseEntity getApplication( return ResponseEntity.ok(details); } - @Operation(summary = "예산지원신청 글 추가 (Swagger 사용 불가. 명세서 참고)") + @Operation(summary = "예산지원신청 글 추가") @PostMapping("/budget/application") @ApiResponses( value = { @@ -123,11 +121,9 @@ public ResponseEntity getApplication( @PreAuthorize( "@boardSecurityChecker.checkMenuAccess(14, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).CREATE_BOARD)") public ResponseEntity createApplication( - @Authenticated Long memberId, - @Valid @RequestPart BudgetApplicationRegisterForm form, - @RequestPart(value = "files") List files) { + @Authenticated Long memberId, @Valid @RequestBody BudgetApplicationRegisterForm form) { - Long newApplicationId = budgetApplicationService.registerApplication(form, files, memberId); + Long newApplicationId = budgetApplicationService.registerApplication(form, memberId); URI location = ServletUriComponentsBuilder.fromCurrentRequest() @@ -137,7 +133,7 @@ public ResponseEntity createApplication( return ResponseEntity.created(location).build(); } - @Operation(summary = "예산지원신청 글 수정 (Swagger 사용 불가. 명세서 참고)") + @Operation(summary = "예산지원신청 글 수정") @PostMapping("/budget/application/{applicationId}") @ApiResponses( value = { @@ -167,9 +163,8 @@ public ResponseEntity createApplication( public ResponseEntity modifyApplication( @Authenticated Long memberId, @PathVariable Long applicationId, - @Valid @RequestPart BudgetApplicationRegisterForm form, - @RequestPart(value = "files") List files) { - budgetApplicationService.updateApplication(applicationId, form, files, memberId); + @Valid @RequestBody BudgetApplicationRegisterForm form) { + budgetApplicationService.updateApplication(applicationId, form, memberId); return ResponseEntity.noContent().build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/web/BudgetHistoryController.java b/resource-server/src/main/java/com/inhabas/api/web/BudgetHistoryController.java index b20bb7cf..a23e4180 100644 --- a/resource-server/src/main/java/com/inhabas/api/web/BudgetHistoryController.java +++ b/resource-server/src/main/java/com/inhabas/api/web/BudgetHistoryController.java @@ -18,10 +18,9 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.inhabas.api.auth.domain.error.ErrorResponse; @@ -143,7 +142,7 @@ public ResponseEntity getBudgetHistory(@PathVariable Lon return ResponseEntity.ok(history); } - @Operation(summary = "회계 내역 추가 (Swagger 사용 불가. 명세서 참고)") + @Operation(summary = "회계 내역 추가") @PostMapping("/budget/history") @ApiResponses({ @ApiResponse(responseCode = "201", description = "'Location' 헤더에 생성된 리소스의 URI 가 포함됩니다."), @@ -160,11 +159,9 @@ public ResponseEntity getBudgetHistory(@PathVariable Lon }) @PreAuthorize("hasRole('SECRETARY')") public ResponseEntity createNewHistory( - @Authenticated Long memberId, - @Valid @RequestPart(value = "form") BudgetHistoryCreateForm form, - @RequestPart(value = "files") List files) { + @Authenticated Long memberId, @Valid @RequestBody BudgetHistoryCreateForm form) { - Long newHistory = budgetHistoryService.createHistory(form, files, memberId); + Long newHistory = budgetHistoryService.createHistory(form, memberId); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/{historyId}") @@ -174,7 +171,7 @@ public ResponseEntity createNewHistory( return ResponseEntity.created(location).build(); } - @Operation(summary = "회계 내역 수정 (Swagger 사용 불가. 명세서 참고)") + @Operation(summary = "회계 내역 수정") @PostMapping("/budget/history/{historyId}") @ApiResponses({ @ApiResponse(responseCode = "204"), @@ -203,10 +200,9 @@ public ResponseEntity createNewHistory( public ResponseEntity modifyHistory( @Authenticated Long memberId, @PathVariable Long historyId, - @Valid @RequestPart(value = "form") BudgetHistoryCreateForm form, - @RequestPart(value = "files") List files) { + @Valid @RequestBody BudgetHistoryCreateForm form) { - budgetHistoryService.modifyHistory(historyId, form, files, memberId); + budgetHistoryService.modifyHistory(historyId, form, memberId); return ResponseEntity.noContent().build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/web/ClubActivityController.java b/resource-server/src/main/java/com/inhabas/api/web/ClubActivityController.java index 971404b7..8e88c03e 100644 --- a/resource-server/src/main/java/com/inhabas/api/web/ClubActivityController.java +++ b/resource-server/src/main/java/com/inhabas/api/web/ClubActivityController.java @@ -8,11 +8,9 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.inhabas.api.auth.domain.error.ErrorResponse; @@ -86,18 +84,12 @@ public ResponseEntity> getClubActivities( value = "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))) }) - @PostMapping(path = "/club/activity", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping("/club/activity") @PreAuthorize( "@boardSecurityChecker.checkMenuAccess(2, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).CREATE_BOARD)") public ResponseEntity writeClubActivity( - @Authenticated Long memberId, - @RequestPart("title") String title, - @RequestPart("content") String content, - @RequestPart(value = "files", required = false) List files) { - - SaveClubActivityDto saveClubActivityDto = new SaveClubActivityDto(title, content, files); - - Long newClubActivityId = clubActivityService.writeClubActivity(memberId, saveClubActivityDto); + @Authenticated Long memberId, @RequestBody SaveClubActivityDto form) { + Long newClubActivityId = clubActivityService.writeClubActivity(memberId, form); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/{boardId}") @@ -168,18 +160,13 @@ public ResponseEntity findClubActivity(@PathVariable Long value = "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) }) - @PostMapping(path = "/club/activity/{boardId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping("/club/activity/{boardId}") @PreAuthorize("@boardSecurityChecker.boardWriterOnly(#boardId) or hasRole('VICE_CHIEF')") public ResponseEntity updateClubActivity( @Authenticated Long memberId, @PathVariable Long boardId, - @RequestPart("title") String title, - @RequestPart("content") String content, - @RequestPart(value = "files", required = false) List files) { - - SaveClubActivityDto saveClubActivityDto = new SaveClubActivityDto(title, content, files); - clubActivityService.updateClubActivity(boardId, saveClubActivityDto); - + @RequestBody SaveClubActivityDto form) { + clubActivityService.updateClubActivity(boardId, form, memberId); return ResponseEntity.noContent().build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/web/ContestBoardController.java b/resource-server/src/main/java/com/inhabas/api/web/ContestBoardController.java index dad20b82..aa814189 100644 --- a/resource-server/src/main/java/com/inhabas/api/web/ContestBoardController.java +++ b/resource-server/src/main/java/com/inhabas/api/web/ContestBoardController.java @@ -11,17 +11,15 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.inhabas.api.auth.domain.error.ErrorResponse; @@ -107,8 +105,8 @@ public ResponseEntity> getContestBoard( @Parameter(description = "검색어 (작성자 이름 or 제목 or 내용)", example = "") @RequestParam(name = "search", defaultValue = "") String search, - @Parameter(description = "페이지", example = "1") - @RequestParam(name = "page", defaultValue = "1") + @Parameter(description = "페이지", example = "0") + @RequestParam(name = "page", defaultValue = "0") int page, @Parameter(description = "페이지당 개수", example = "4") @RequestParam(name = "size", defaultValue = "4") @@ -166,7 +164,7 @@ public ResponseEntity getContestBoard( } @Operation(summary = "공모전 게시글 추가") - @PostMapping(path = "/contest/{contestType}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping("/contest/{contestType}") @PreAuthorize( "@boardSecurityChecker.checkMenuAccess(#contestType.menuId, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).CREATE_BOARD)") @ApiResponses( @@ -196,22 +194,8 @@ public ResponseEntity getContestBoard( public ResponseEntity writeContestBoard( @Authenticated Long memberId, @PathVariable ContestType contestType, - @Valid @RequestPart("form") SaveContestBoardDto form, - @RequestPart(value = "files", required = false) List files) { - - SaveContestBoardDto saveContestBoardDto = - SaveContestBoardDto.builder() - .contestFieldId(form.getContestFieldId()) - .title(form.getTitle()) - .content(form.getContent()) - .association(form.getAssociation()) - .topic(form.getTopic()) - .dateContestStart(form.getDateContestStart()) - .dateContestEnd(form.getDateContestEnd()) - .files(files) - .build(); - Long newContestBoardId = - contestBoardService.writeContestBoard(memberId, contestType, saveContestBoardDto); + @Valid @RequestBody SaveContestBoardDto form) { + Long newContestBoardId = contestBoardService.writeContestBoard(memberId, contestType, form); // 뒤에 추가 URI 생성 URI location = @@ -224,9 +208,7 @@ public ResponseEntity writeContestBoard( } @Operation(summary = "공모전 게시글 수정") - @PostMapping( - path = "/contest/{contestType}/{boardId}", - consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(path = "/contest/{contestType}/{boardId}") @PreAuthorize("@boardSecurityChecker.boardWriterOnly(#boardId) or hasRole('VICE_CHIEF')") @ApiResponses( value = { @@ -256,22 +238,8 @@ public ResponseEntity updateContestBoard( @Authenticated Long memberId, @PathVariable ContestType contestType, @PathVariable Long boardId, - @Valid @RequestPart SaveContestBoardDto form, - @RequestPart(value = "files", required = false) List files) { - - SaveContestBoardDto saveContestBoardDto = - SaveContestBoardDto.builder() - .contestFieldId(form.getContestFieldId()) - .title(form.getTitle()) - .content(form.getContent()) - .association(form.getAssociation()) - .topic(form.getTopic()) - .dateContestStart(form.getDateContestStart()) - .dateContestEnd(form.getDateContestEnd()) - .files(files) - .build(); - - contestBoardService.updateContestBoard(boardId, contestType, saveContestBoardDto); + @Valid @RequestBody SaveContestBoardDto form) { + contestBoardService.updateContestBoard(boardId, contestType, form, memberId); return ResponseEntity.noContent().build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/web/FileController.java b/resource-server/src/main/java/com/inhabas/api/web/FileController.java new file mode 100644 index 00000000..f7f6bbdf --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/web/FileController.java @@ -0,0 +1,71 @@ +package com.inhabas.api.web; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.inhabas.api.auth.domain.error.ErrorResponse; +import com.inhabas.api.domain.file.dto.FileDownloadDto; +import com.inhabas.api.domain.file.usecase.BoardFileService; +import com.inhabas.api.global.dto.PagedResponseDto; +import com.inhabas.api.web.argumentResolver.Authenticated; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Slf4j +@Tag(name = "첨부파일 관리") +@RestController +@RequiredArgsConstructor +public class FileController { + + private final BoardFileService boardFileService; + + @Operation(summary = "게시판 첨부파일 업로드") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "201", + content = {@Content(schema = @Schema(implementation = PagedResponseDto.class))}), + @ApiResponse( + responseCode = "400", + description = "입력값이 없거나, 타입이 유효하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + @PostMapping(value = "/file/upload/{menuId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PreAuthorize( + "@boardSecurityChecker.checkMenuAccess(#menuId, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).CREATE_BOARD)") + public FileDownloadDto uploadBoardFile( + @Authenticated Long memberId, + @PathVariable Integer menuId, + @RequestParam("file") MultipartFile file) { + return boardFileService.upload(menuId, file, memberId); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/web/MyProfileController.java b/resource-server/src/main/java/com/inhabas/api/web/MyProfileController.java index 34e70065..73fd1eb3 100644 --- a/resource-server/src/main/java/com/inhabas/api/web/MyProfileController.java +++ b/resource-server/src/main/java/com/inhabas/api/web/MyProfileController.java @@ -117,7 +117,7 @@ public ResponseEntity updateMyProfileIntro( }) @PostMapping(value = "/myInfo/picture", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity updateMyProfileImage( - @Authenticated Long memberId, @RequestPart(value = "picture") MultipartFile file) { + @Authenticated Long memberId, @RequestParam(value = "picture") MultipartFile file) { memberProfileService.updateMyProfileImage(memberId, file); return ResponseEntity.noContent().build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/web/NormalBoardController.java b/resource-server/src/main/java/com/inhabas/api/web/NormalBoardController.java index 3259532e..f2f6f044 100644 --- a/resource-server/src/main/java/com/inhabas/api/web/NormalBoardController.java +++ b/resource-server/src/main/java/com/inhabas/api/web/NormalBoardController.java @@ -11,11 +11,9 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.inhabas.api.auth.domain.error.ErrorResponse; @@ -156,7 +154,7 @@ public ResponseEntity getBoard( } @Operation(summary = "게시글 추가") - @PostMapping(path = "/board/{boardType}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping("/board/{boardType}") @PreAuthorize( "@boardSecurityChecker.checkMenuAccess(#boardType.menuId, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).CREATE_BOARD)") @ApiResponses( @@ -186,16 +184,8 @@ public ResponseEntity getBoard( public ResponseEntity addBoard( @Authenticated Long memberId, @PathVariable NormalBoardType boardType, - @Valid @RequestPart("form") SaveNormalBoardDto form, - @RequestPart(value = "files", required = false) List files) { - SaveNormalBoardDto saveNormalBoardDto = - SaveNormalBoardDto.builder() - .title(form.getTitle()) - .content(form.getContent()) - .pinOption(form.getPinOption()) - .files(files) - .build(); - Long newNormalBoardId = normalBoardService.write(memberId, boardType, saveNormalBoardDto); + @Valid @RequestBody SaveNormalBoardDto form) { + Long newNormalBoardId = normalBoardService.write(memberId, boardType, form); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/{boardId}") @@ -206,9 +196,7 @@ public ResponseEntity addBoard( } @Operation(summary = "게시글 수정") - @PostMapping( - value = "/board/{boardType}/{boardId}", - consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(value = "/board/{boardType}/{boardId}") @ApiResponses({ @ApiResponse(responseCode = "204"), @ApiResponse( @@ -237,16 +225,8 @@ public ResponseEntity updateBoard( @Authenticated Long memberId, @PathVariable NormalBoardType boardType, @PathVariable Long boardId, - @Valid @RequestPart("form") SaveNormalBoardDto form, - @RequestPart(value = "files", required = false) List files) { - SaveNormalBoardDto saveNormalBoardDto = - SaveNormalBoardDto.builder() - .title(form.getTitle()) - .content(form.getContent()) - .pinOption(form.getPinOption()) - .files(files) - .build(); - normalBoardService.update(boardId, boardType, saveNormalBoardDto); + @Valid @RequestBody SaveNormalBoardDto form) { + normalBoardService.update(boardId, boardType, form, memberId); return ResponseEntity.noContent().build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/web/ProjectBoardController.java b/resource-server/src/main/java/com/inhabas/api/web/ProjectBoardController.java index 093b3e96..0dc8f88a 100644 --- a/resource-server/src/main/java/com/inhabas/api/web/ProjectBoardController.java +++ b/resource-server/src/main/java/com/inhabas/api/web/ProjectBoardController.java @@ -11,11 +11,9 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.inhabas.api.auth.domain.error.ErrorResponse; @@ -156,7 +154,7 @@ public ResponseEntity getBoard( } @Operation(summary = "프로젝트 게시글 추가") - @PostMapping(path = "/project/{projectBoardType}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping("/project/{projectBoardType}") @PreAuthorize( "@boardSecurityChecker.checkMenuAccess(#projectBoardType.menuId, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).CREATE_BOARD)") @ApiResponses( @@ -186,17 +184,8 @@ public ResponseEntity getBoard( public ResponseEntity addBoard( @Authenticated Long memberId, @PathVariable ProjectBoardType projectBoardType, - @Valid @RequestPart("form") SaveProjectBoardDto form, - @RequestPart(value = "files", required = false) List files) { - SaveProjectBoardDto saveProjectBoardDto = - SaveProjectBoardDto.builder() - .title(form.getTitle()) - .content(form.getContent()) - .pinOption(form.getPinOption()) - .files(files) - .build(); - Long newProjectBoardId = - projectBoardService.write(memberId, projectBoardType, saveProjectBoardDto); + @Valid @RequestBody SaveProjectBoardDto form) { + Long newProjectBoardId = projectBoardService.write(memberId, projectBoardType, form); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/{boardId}") @@ -207,9 +196,7 @@ public ResponseEntity addBoard( } @Operation(summary = "프로젝트 게시글 수정") - @PostMapping( - value = "/project/{projectBoardType}/{boardId}", - consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(value = "/project/{projectBoardType}/{boardId}") @ApiResponses({ @ApiResponse(responseCode = "200"), @ApiResponse( @@ -238,16 +225,8 @@ public ResponseEntity updateBoard( @Authenticated Long memberId, @PathVariable ProjectBoardType projectBoardType, @PathVariable Long boardId, - @Valid @RequestPart("form") SaveProjectBoardDto form, - @RequestPart(value = "files", required = false) List files) { - SaveProjectBoardDto saveProjectBoardDto = - SaveProjectBoardDto.builder() - .title(form.getTitle()) - .content(form.getContent()) - .pinOption(form.getPinOption()) - .files(files) - .build(); - projectBoardService.update(boardId, projectBoardType, saveProjectBoardDto); + @Valid @RequestBody SaveProjectBoardDto form) { + projectBoardService.update(boardId, projectBoardType, form, memberId); return ResponseEntity.noContent().build(); } diff --git a/resource-server/src/main/java/com/inhabas/api/web/ScholarshipController.java b/resource-server/src/main/java/com/inhabas/api/web/ScholarshipController.java new file mode 100644 index 00000000..9ba04027 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/web/ScholarshipController.java @@ -0,0 +1,233 @@ +package com.inhabas.api.web; + +import java.net.URI; +import java.util.List; + +import javax.validation.Valid; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import com.inhabas.api.auth.domain.error.ErrorResponse; +import com.inhabas.api.domain.scholarship.domain.ScholarshipBoardType; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipBoardDto; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDetailDto; +import com.inhabas.api.domain.scholarship.dto.ScholarshipBoardDto; +import com.inhabas.api.domain.scholarship.usecase.ScholarshipBoardService; +import com.inhabas.api.global.dto.PageInfoDto; +import com.inhabas.api.global.dto.PagedResponseDto; +import com.inhabas.api.global.util.PageUtil; +import com.inhabas.api.web.argumentResolver.Authenticated; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Slf4j +@Tag(name = "장학회 게시판 관리") +@RestController +@RequiredArgsConstructor +public class ScholarshipController { + + private final ScholarshipBoardService scholarshipBoardService; + + @Operation(summary = "장학회 게시글 목록 조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + content = {@Content(schema = @Schema(implementation = PagedResponseDto.class))}), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + @GetMapping("/scholarship/{boardType}") + @PreAuthorize( + "@boardSecurityChecker.checkMenuAccess(#boardType.menuId, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).READ_BOARD_LIST)") + public ResponseEntity> getBoardList( + @Parameter(description = "페이지", example = "0") + @RequestParam(name = "page", defaultValue = "0") + int page, + @Parameter(description = "페이지당 개수", example = "10") + @RequestParam(name = "size", defaultValue = "10") + int size, + @Parameter(description = "검색어 (작성자 이름 or 제목 or 내용)", example = "") + @RequestParam(name = "search", defaultValue = "") + String search, + @PathVariable ScholarshipBoardType boardType) { + + Pageable pageable = PageRequest.of(page, size); + List allDtoList = scholarshipBoardService.getPosts(boardType, search); + List pagedDtoList = PageUtil.getPagedDtoList(pageable, allDtoList); + + PageImpl ScholarshipBoardDtoPage = + new PageImpl<>(pagedDtoList, pageable, allDtoList.size()); + PageInfoDto pageInfoDto = new PageInfoDto(ScholarshipBoardDtoPage); + + return ResponseEntity.ok(new PagedResponseDto<>(pageInfoDto, pagedDtoList)); + } + + @Operation(summary = "장학회 게시글 단일 조회") + @GetMapping("/scholarship/{boardType}/{boardId}") + @PreAuthorize( + "@boardSecurityChecker.checkMenuAccess(#boardType.menuId, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).READ_BOARD)") + @ApiResponses({ + @ApiResponse( + responseCode = "200", + content = {@Content(schema = @Schema(implementation = ScholarshipBoardDto.class))}), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + public ResponseEntity getBoard( + @PathVariable Long boardId, + @PathVariable ScholarshipBoardType boardType, + @Authenticated Long memberId) { + + return ResponseEntity.ok(scholarshipBoardService.getPost(boardType, boardId, memberId)); + } + + @Operation(summary = "장학회 게시글 추가") + @PostMapping("/scholarship/{boardType}") + @PreAuthorize( + "@boardSecurityChecker.checkMenuAccess(#boardType.menuId, T(com.inhabas.api.domain.board.usecase.BoardSecurityChecker).CREATE_BOARD)") + @ApiResponses( + value = { + @ApiResponse(responseCode = "201", description = "'Location' 헤더에 생성된 리소스의 URI 가 포함됩니다."), + @ApiResponse( + responseCode = "400", + description = "입력값이 없거나, 타입이 유효하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + public ResponseEntity addBoard( + @Authenticated Long memberId, + @PathVariable ScholarshipBoardType boardType, + @Valid @RequestBody SaveScholarshipBoardDto form) { + Long newScholarshipBoardId = scholarshipBoardService.write(boardType, form, memberId); + URI location = + ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{boardId}") + .buildAndExpand(newScholarshipBoardId) + .toUri(); + + return ResponseEntity.created(location).build(); + } + + @Operation(summary = "장학회 게시글 수정") + @PostMapping("/scholarship/{boardType}/{boardId}") + @ApiResponses({ + @ApiResponse(responseCode = "200"), + @ApiResponse( + responseCode = "400 ", + description = "입력값이 없거나, 타입이 유효하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + @PreAuthorize("@boardSecurityChecker.boardWriterOnly(#boardId) or hasRole('VICE_CHIEF')") + public ResponseEntity updateBoard( + @Authenticated Long memberId, + @PathVariable ScholarshipBoardType boardType, + @PathVariable Long boardId, + @Valid @RequestBody SaveScholarshipBoardDto form) { + scholarshipBoardService.update(boardId, boardType, form, memberId); + return ResponseEntity.noContent().build(); + } + + @Operation(summary = "장학회 게시글 삭제") + @DeleteMapping("/scholarship/{boardType}/{boardId}") + @ApiResponses({ + @ApiResponse(responseCode = "204"), + @ApiResponse( + responseCode = "400 ", + description = "입력값이 없거나, 타입이 유효하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + @PreAuthorize("@boardSecurityChecker.boardWriterOnly(#boardId) or hasRole('VICE_CHIEF')") + public ResponseEntity deleteBoard( + @Authenticated Long memberId, + @PathVariable ScholarshipBoardType boardType, + @PathVariable Long boardId) { + + scholarshipBoardService.delete(boardType, boardId); + return ResponseEntity.noContent().build(); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/web/ScholarshipHistoryController.java b/resource-server/src/main/java/com/inhabas/api/web/ScholarshipHistoryController.java new file mode 100644 index 00000000..7207e851 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/web/ScholarshipHistoryController.java @@ -0,0 +1,155 @@ +package com.inhabas.api.web; + +import java.net.URI; +import java.util.List; + +import javax.validation.Valid; + +import lombok.RequiredArgsConstructor; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import com.inhabas.api.auth.domain.error.ErrorResponse; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipHistoryDto; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.YearlyData; +import com.inhabas.api.domain.scholarship.usecase.ScholarshipHistoryService; +import com.inhabas.api.web.argumentResolver.Authenticated; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Tag(name = "장학회 연혁", description = "장학회 소개 관련") +@RestController +@RequiredArgsConstructor +public class ScholarshipHistoryController { + + private final ScholarshipHistoryService scholarshipHistoryService; + + @Operation(summary = "장학회 연혁 조회") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + content = { + @Content(schema = @Schema(implementation = YearlyData.class, type = "array")) + }), + }) + @SecurityRequirements(value = {}) + @GetMapping("/scholarship/histories") + public ResponseEntity> getScholarHistories() { + + List dtoList = scholarshipHistoryService.getScholarshipHistories(); + return ResponseEntity.ok(dtoList); + } + + @Operation(summary = "장학회 연혁 생성") + @ApiResponses( + value = { + @ApiResponse(responseCode = "201", description = "'Location' 헤더에 생성된 리소스의 URI 가 포함됩니다."), + @ApiResponse( + responseCode = "400 ", + description = "입력값이 없거나, 타입이 유효하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))) + }) + @PostMapping("/scholarship/history") + public ResponseEntity writeScholarshipHistory( + @Authenticated Long memberId, + @Valid @RequestBody SaveScholarshipHistoryDto saveScholarshipHistoryDto) { + + Long newScholarshipHistoryId = + scholarshipHistoryService.writeScholarshipHistory(memberId, saveScholarshipHistoryDto); + URI location = + ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{scholarshipHistoryId}") + .buildAndExpand(newScholarshipHistoryId) + .toUri(); + return ResponseEntity.created(location).build(); + } + + @Operation(summary = "장학회 연혁 수정") + @ApiResponses( + value = { + @ApiResponse(responseCode = "204"), + @ApiResponse( + responseCode = "400 ", + description = "입력값이 없거나, 타입이 유효하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + @PutMapping("/scholarship/history/{scholarshipHistoryId}") + public ResponseEntity updateScholarshipHistory( + @PathVariable Long scholarshipHistoryId, + @Authenticated Long memberId, + @Valid @RequestBody SaveScholarshipHistoryDto saveScholarshipHistoryDto) { + + scholarshipHistoryService.updateScholarshipHistory( + memberId, scholarshipHistoryId, saveScholarshipHistoryDto); + return ResponseEntity.noContent().build(); + } + + @Operation(summary = "장학회 연혁 삭제", description = "장학회 연혁 삭제") + @ApiResponses( + value = { + @ApiResponse(responseCode = "204"), + @ApiResponse( + responseCode = "400 ", + description = "입력값이 없거나, 타입이 유효하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 400, \"code\": \"G003\", \"message\": \"입력값이 없거나, 타입이 유효하지 않습니다.\"}"))), + @ApiResponse( + responseCode = "404", + description = "데이터가 존재하지 않습니다.", + content = + @Content( + schema = @Schema(implementation = ErrorResponse.class), + examples = + @ExampleObject( + value = + "{\"status\": 404, \"code\": \"G004\", \"message\": \"데이터가 존재하지 않습니다.\"}"))) + }) + @DeleteMapping("/scholarship/history/{scholarshipHistoryId}") + public ResponseEntity deleteScholarshipHistory(@PathVariable Long scholarshipHistoryId) { + + scholarshipHistoryService.deleteScholarshipHistory(scholarshipHistoryId); + return ResponseEntity.noContent().build(); + } +} diff --git a/resource-server/src/main/java/com/inhabas/api/web/converter/ScholarshipBoardConverter.java b/resource-server/src/main/java/com/inhabas/api/web/converter/ScholarshipBoardConverter.java new file mode 100644 index 00000000..2e7c48a5 --- /dev/null +++ b/resource-server/src/main/java/com/inhabas/api/web/converter/ScholarshipBoardConverter.java @@ -0,0 +1,38 @@ +package com.inhabas.api.web.converter; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import com.inhabas.api.auth.domain.error.businessException.InvalidInputException; +import com.inhabas.api.auth.domain.error.businessException.NotFoundException; +import com.inhabas.api.domain.scholarship.domain.ScholarshipBoardType; + +public class ScholarshipBoardConverter { + + @Component + public static class StringToScholarshipTypeConverter + implements Converter { + + @Override + public ScholarshipBoardType convert(String source) { + if (!StringUtils.hasText(source)) { + throw new InvalidInputException(); + } + try { + return ScholarshipBoardType.valueOf(source.trim().toUpperCase()); + } catch (IllegalArgumentException e) { + throw new NotFoundException(); + } + } + } + + @Component + public static class ScholarshipBoardTypeToStringConverter + implements Converter { + @Override + public String convert(ScholarshipBoardType source) { + return source != null ? source.toString().toLowerCase() : null; + } + } +} diff --git a/resource-server/src/test/java/com/inhabas/api/domain/scholarship/domain/ScholarshipHistoryTest.java b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/domain/ScholarshipHistoryTest.java new file mode 100644 index 00000000..514a2db6 --- /dev/null +++ b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/domain/ScholarshipHistoryTest.java @@ -0,0 +1,42 @@ +package com.inhabas.api.domain.scholarship.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDateTime; + +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.domain.member.domain.entity.MemberTest; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipHistoryDto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ScholarshipHistoryTest { + + @DisplayName("Dto 를 사용해서 scholarshipHistory를 수정한다.") + @Test + void update() { + // given + Member writer = MemberTest.chiefMember(); + ScholarshipHistory scholarshipHistory = + ScholarshipHistory.builder() + .writer(writer) + .title("oldTitle") + .dateHistory(LocalDateTime.now()) + .build(); + + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + SaveScholarshipHistoryDto.builder().title("title").dateHistory(LocalDateTime.now()).build(); + + // when + scholarshipHistory.update(writer, saveScholarshipHistoryDto); + + // then + assertThat(scholarshipHistory) + .extracting( + ScholarshipHistory -> scholarshipHistory.getTitle(), + ScholarshipHistory -> scholarshipHistory.getDateHistory()) + .containsExactly( + saveScholarshipHistoryDto.getTitle(), saveScholarshipHistoryDto.getDateHistory()); + } +} diff --git a/resource-server/src/test/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipHistoryDtoTest.java b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipHistoryDtoTest.java new file mode 100644 index 00000000..f6fadbd9 --- /dev/null +++ b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/dto/SaveScholarshipHistoryDtoTest.java @@ -0,0 +1,46 @@ +package com.inhabas.api.domain.scholarship.dto; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class SaveScholarshipHistoryDtoTest { + private static ValidatorFactory validatorFactory; + private static Validator validator; + + @BeforeAll + public static void init() { + validatorFactory = Validation.buildDefaultValidatorFactory(); + validator = validatorFactory.getValidator(); + } + + @AfterAll + public static void close() { + validatorFactory.close(); + } + + @DisplayName("title, dateHistory 가 null 이면 validation 실패") + @Test + void NotNull_Test() { + // given + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + SaveScholarshipHistoryDto.builder().title(null).dateHistory(null).build(); + + // when + Set> violations = + validator.validate(saveScholarshipHistoryDto); + + // then + assertThat(violations).hasSize(2); + } +} diff --git a/resource-server/src/test/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryTest.java b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryTest.java new file mode 100644 index 00000000..2d31a275 --- /dev/null +++ b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/repository/ScholarshipHistoryRepositoryTest.java @@ -0,0 +1,57 @@ +package com.inhabas.api.domain.scholarship.repository; + +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.util.ReflectionTestUtils; + +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; +import com.inhabas.api.domain.member.domain.entity.MemberTest; +import com.inhabas.api.domain.scholarship.domain.ScholarshipHistory; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.Data; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.YearlyData; +import com.inhabas.testAnnotataion.DefaultDataJpaTest; +import org.assertj.core.api.Assertions; + +import org.junit.jupiter.api.Test; + +@DefaultDataJpaTest +public class ScholarshipHistoryRepositoryTest { + + @Autowired ScholarshipHistoryRepository scholarshipHistoryRepository; + @Autowired MemberRepository memberRepository; + @Autowired TestEntityManager em; + + @Test + void getYearlyData() { + // given + Member writer = MemberTest.chiefMember(); + ReflectionTestUtils.setField(writer, "id", 1L); + memberRepository.save(writer); + ScholarshipHistory scholarshipHistory = + new ScholarshipHistory(writer, "title", LocalDateTime.now()); + ReflectionTestUtils.setField(scholarshipHistory, "id", 1L); + ScholarshipHistory savedScholarshipHistory = + scholarshipHistoryRepository.save(scholarshipHistory); + Data data = + new Data(1L, savedScholarshipHistory.getTitle(), savedScholarshipHistory.getDateHistory()); + List savedData = + List.of(new YearlyData(savedScholarshipHistory.getDateHistory().getYear(), List.of(data))); + + // when + scholarshipHistoryRepository.save(scholarshipHistory); + List yearlyData = scholarshipHistoryRepository.getYearlyData(); + + // then + Assertions.assertThat(yearlyData.get(0).year).isEqualTo(savedData.get(0).year); + Assertions.assertThat(yearlyData.get(0).data.get(0).id) + .isEqualTo(savedData.get(0).data.get(0).id); + Assertions.assertThat(yearlyData.get(0).data.get(0).dateHistory) + .isEqualTo(savedData.get(0).data.get(0).dateHistory); + Assertions.assertThat(yearlyData.get(0).data.get(0).title) + .isEqualTo(savedData.get(0).data.get(0).title); + } +} diff --git a/resource-server/src/test/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryServiceImplTest.java b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryServiceImplTest.java new file mode 100644 index 00000000..45b87443 --- /dev/null +++ b/resource-server/src/test/java/com/inhabas/api/domain/scholarship/usecase/ScholarshipHistoryServiceImplTest.java @@ -0,0 +1,127 @@ +package com.inhabas.api.domain.scholarship.usecase; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import org.springframework.test.util.ReflectionTestUtils; + +import com.inhabas.api.auth.domain.oauth2.member.domain.entity.Member; +import com.inhabas.api.auth.domain.oauth2.member.repository.MemberRepository; +import com.inhabas.api.domain.member.domain.entity.MemberTest; +import com.inhabas.api.domain.scholarship.domain.ScholarshipHistory; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipHistoryDto; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepository; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.Data; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.YearlyData; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(MockitoExtension.class) +public class ScholarshipHistoryServiceImplTest { + + @InjectMocks ScholarshipHistoryServiceImpl scholarshipHistoryService; + @Mock ScholarshipHistoryRepository scholarshipHistoryRepository; + @Mock MemberRepository memberRepository; + + @DisplayName("장학회 연혁 조회 성공") + @Test + void getScholarshipHistories() { + // given + YearlyData yearlyData = + new YearlyData(2023, List.of(new Data(1L, "title", LocalDateTime.now()))); + List yearlyDataList = List.of(yearlyData); + given(scholarshipHistoryRepository.getYearlyData()).willReturn(yearlyDataList); + + // when + List result = scholarshipHistoryService.getScholarshipHistories(); + + // then + then(scholarshipHistoryRepository).should().getYearlyData(); + assertThat(result).hasSize(1); + } + + @DisplayName("장학회 연혁 생성 성공") + @Test + void writeScholarshipHistory() { + // given + Long memberId = 1L; + Member member = MemberTest.chiefMember(); // 필요한 속성으로 Member 객체 초기화 + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + new SaveScholarshipHistoryDto("title", LocalDateTime.now()); + + given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + given(scholarshipHistoryRepository.save(any(ScholarshipHistory.class))) + .willAnswer( + invocation -> { + ScholarshipHistory scholarshipHistory = invocation.getArgument(0); + ReflectionTestUtils.setField(scholarshipHistory, "id", 1L); + return scholarshipHistory; + }); + + // when + Long resultId = + scholarshipHistoryService.writeScholarshipHistory(memberId, saveScholarshipHistoryDto); + + // then + then(memberRepository).should().findById(memberId); + then(scholarshipHistoryRepository).should().save(any(ScholarshipHistory.class)); + assertThat(resultId).isEqualTo(1L); + } + + @DisplayName("장학회 연혁 수정 성공") + @Test + void updateScholarshipHistory() { + // given + Member member = MemberTest.chiefMember(); + ScholarshipHistory scholarshipHistory = + ScholarshipHistory.builder() + .writer(member) + .title("title") + .dateHistory(LocalDateTime.now()) + .build(); + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + new SaveScholarshipHistoryDto("title", LocalDateTime.now()); + given(scholarshipHistoryRepository.findById(any())) + .willReturn(Optional.ofNullable(scholarshipHistory)); + given(memberRepository.findById(any())).willReturn(Optional.of(member)); + + // when + scholarshipHistoryService.updateScholarshipHistory(1L, 1L, saveScholarshipHistoryDto); + + // then + then(scholarshipHistoryRepository).should().findById(any()); + then(memberRepository).should().findById(any()); + } + + @DisplayName("장학회 연혁 삭제 성공") + @Test + void deleteScholarshipHistory() { + // given + ScholarshipHistory scholarshipHistory = + ScholarshipHistory.builder() + .writer(MemberTest.chiefMember()) + .title("title") + .dateHistory(LocalDateTime.now()) + .build(); + given(scholarshipHistoryRepository.findById(any())) + .willReturn(Optional.ofNullable(scholarshipHistory)); + + // when + scholarshipHistoryService.deleteScholarshipHistory(any()); + + // then + then(scholarshipHistoryRepository).should().findById(any()); + then(scholarshipHistoryRepository).should().delete(any()); + } +} diff --git a/resource-server/src/test/java/com/inhabas/api/web/ScholarshipHistoryControllerTest.java b/resource-server/src/test/java/com/inhabas/api/web/ScholarshipHistoryControllerTest.java new file mode 100644 index 00000000..c4da4cd9 --- /dev/null +++ b/resource-server/src/test/java/com/inhabas/api/web/ScholarshipHistoryControllerTest.java @@ -0,0 +1,247 @@ +package com.inhabas.api.web; + +import static com.inhabas.api.auth.domain.error.ErrorCode.INVALID_INPUT_VALUE; +import static com.inhabas.api.auth.domain.error.ErrorCode.NOT_FOUND; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.inhabas.api.auth.domain.error.businessException.InvalidInputException; +import com.inhabas.api.auth.domain.error.businessException.NotFoundException; +import com.inhabas.api.domain.scholarship.dto.SaveScholarshipHistoryDto; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.Data; +import com.inhabas.api.domain.scholarship.repository.ScholarshipHistoryRepositoryImpl.YearlyData; +import com.inhabas.api.domain.scholarship.usecase.ScholarshipHistoryService; +import com.inhabas.testAnnotataion.NoSecureWebMvcTest; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@NoSecureWebMvcTest(ScholarshipHistoryController.class) +public class ScholarshipHistoryControllerTest { + + @Autowired private MockMvc mvc; + @Autowired private ObjectMapper objectMapper; + @MockBean private ScholarshipHistoryService scholarshipHistoryService; + + private String jsonOf(Object response) throws JsonProcessingException { + return objectMapper.writeValueAsString(response); + } + + @DisplayName("장학회 연혁 조회 성공 200") + @Test + void getScholarHistoriesTest() throws Exception { + // given + YearlyData yearlyData = + new YearlyData(2024, List.of(new Data(1L, "title", LocalDateTime.now()))); + List yearlyDataList = List.of(yearlyData); + given(scholarshipHistoryService.getScholarshipHistories()).willReturn(yearlyDataList); + + // when + String response = + mvc.perform(get("/scholarship/histories")) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + + // then + assertThat(response).isEqualTo(jsonOf(yearlyDataList)); + } + + @DisplayName("장학회 연혁 생성 성공 201") + @Test + void writeScholarshipHistoryTest() throws Exception { + // given + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + SaveScholarshipHistoryDto.builder() + .title("good title") + .dateHistory(LocalDateTime.parse("2023-11-01T00:00:00")) + .build(); + given(scholarshipHistoryService.writeScholarshipHistory(any(), any())).willReturn(1L); + + // when + String header = + mvc.perform( + post("/scholarship/history") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonOf(saveScholarshipHistoryDto))) + .andExpect(status().isCreated()) + .andReturn() + .getResponse() + .getHeader("Location"); + + // then + assertThat(header).contains("/scholarship/history/1"); + } + + @DisplayName("장학회 연혁 생성 데이터가 올바르지 않다면 400") + @Test + void writeScholarshipHistory_Invalid_Input() throws Exception { + // given + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + SaveScholarshipHistoryDto.builder() + .title("meaningless") + .dateHistory(LocalDateTime.now()) + .build(); + doThrow(InvalidInputException.class) + .when(scholarshipHistoryService) + .writeScholarshipHistory(any(), any()); + + // when + String response = + mvc.perform( + post("/scholarship/history") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonOf(saveScholarshipHistoryDto))) + .andExpect(status().isBadRequest()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + + // then + assertThat(response).contains(INVALID_INPUT_VALUE.getMessage()); + } + + @DisplayName("장학회 연혁 수정 성공 204") + @Test + void updateScholarshipHistoryTest() throws Exception { + // given + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + SaveScholarshipHistoryDto.builder() + .title("meaningless") + .dateHistory(LocalDateTime.now()) + .build(); + doNothing().when(scholarshipHistoryService).updateScholarshipHistory(any(), any(), any()); + + // when then + mvc.perform( + put("/scholarship/history/{scholarshipHistoryId}", 1) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonOf(saveScholarshipHistoryDto))) + .andExpect(status().isNoContent()); + } + + @DisplayName("장학회 연혁 수정 데이터가 올바르지 않다면 400") + @Test + void updateScholarshipHistory_Invalid_Input() throws Exception { + // given + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + SaveScholarshipHistoryDto.builder() + .title("meaningless") + .dateHistory(LocalDateTime.now()) + .build(); + doThrow(InvalidInputException.class) + .when(scholarshipHistoryService) + .updateScholarshipHistory(any(), any(), any()); + + // when + String response = + mvc.perform( + put("/scholarship/history/{scholarshipHistoryId}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonOf(saveScholarshipHistoryDto))) + .andExpect(status().isBadRequest()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + + // then + assertThat(response).contains(INVALID_INPUT_VALUE.getMessage()); + } + + @DisplayName("장학회 연혁 수정 해당 id가 존재하지 않다면 404") + @Test + void updateScholarshipHistory_Not_Found() throws Exception { + // given + SaveScholarshipHistoryDto saveScholarshipHistoryDto = + SaveScholarshipHistoryDto.builder() + .title("meaningless") + .dateHistory(LocalDateTime.now()) + .build(); + doThrow(NotFoundException.class) + .when(scholarshipHistoryService) + .updateScholarshipHistory(any(), any(), any()); + + // when + String response = + mvc.perform( + put("/scholarship/history/{scholarshipHistoryId}", 1L) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonOf(saveScholarshipHistoryDto))) + .andExpect(status().isNotFound()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + + // then + assertThat(response).contains(NOT_FOUND.getMessage()); + } + + @DisplayName("장학회 연혁 삭제 성공 204") + @Test + void deleteScholarshipHistoryTest() throws Exception { + // given + doNothing().when(scholarshipHistoryService).deleteScholarshipHistory(any()); + + // when then + mvc.perform(delete("/scholarship/history/{scholarshipHistoryId}", 1L)) + .andExpect(status().isNoContent()); + } + + @DisplayName("장학회 연혁 삭제 데이터가 올바르지 않다면 400") + @Test + void deleteScholarshipHistory_Invalid_Input() throws Exception { + // given + doNothing().when(scholarshipHistoryService).deleteScholarshipHistory(any()); + + // when + String response = + mvc.perform(delete("/scholarship/history/{scholarshipHistoryId}", "invalid")) + .andExpect(status().isBadRequest()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + + // then + assertThat(response).contains(INVALID_INPUT_VALUE.getMessage()); + } + + @DisplayName("장학회 연혁 삭제 해당 id가 없다면 404") + @Test + void deleteScholarshipHistory_Not_Found() throws Exception { + // given + doThrow(NotFoundException.class) + .when(scholarshipHistoryService) + .deleteScholarshipHistory(any()); + + // when + String response = + mvc.perform(delete("/scholarship/history/{scholarshipHistoryId}", 1L)) + .andExpect(status().isNotFound()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + + // then + assertThat(response).contains(NOT_FOUND.getMessage()); + } +}