diff --git a/src/main/java/ddingdong/ddingdongBE/domain/club/entity/Club.java b/src/main/java/ddingdong/ddingdongBE/domain/club/entity/Club.java index 5917d8d03..04a0c4ea4 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/club/entity/Club.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/club/entity/Club.java @@ -128,4 +128,8 @@ public void addClubMembers(List clubMembers) { this.clubMembers.addAll(clubMembers); clubMembers.forEach(clubMember -> clubMember.setClubForConvenience(this)); } + + public void removeAll(final List deletedMembers) { + this.clubMembers.removeAll(deletedMembers); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/repository/ClubMemberRepository.java b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/repository/ClubMemberRepository.java index 17d3de1b5..39b51d68c 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/repository/ClubMemberRepository.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/repository/ClubMemberRepository.java @@ -1,8 +1,13 @@ package ddingdong.ddingdongBE.domain.clubmember.repository; import ddingdong.ddingdongBE.domain.clubmember.entity.ClubMember; +import java.util.Collection; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface ClubMemberRepository extends JpaRepository { + List findByIdIn(Collection ids); + + List findByClubId(Long clubId); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/ClubMemberService.java b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/ClubMemberService.java index 0513709c2..b36e04387 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/ClubMemberService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/ClubMemberService.java @@ -14,4 +14,8 @@ public interface ClubMemberService { void delete(ClubMember clubMember); void save(ClubMember clubMember); + + void updateAll(List updateClubMemberInfos); + + List getByClubId(Long clubId); } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceImpl.java b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceImpl.java index 8ec8dd366..198fa4167 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceImpl.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceImpl.java @@ -43,18 +43,22 @@ public AllClubMemberInfoQuery getAllMyClubMember(Long userId) { @Transactional public void updateMemberList(UpdateClubMemberListCommand command) { Club club = clubService.getByUserId(command.userId()); - List updatedClubMembers = + List updateClubMemberInfos = excelFileService.extractClubMembersInformation(club, command.clubMemberListFile()); - List clubMembers = club.getClubMembers(); - Set updatedMemberIds = updatedClubMembers.stream() + List clubMembers = clubMemberService.getByClubId(club.getId()); + Set updatedMemberInfoIds = updateClubMemberInfos.stream() .map(ClubMember::getId) .collect(Collectors.toSet()); Set currentMemberIds = clubMembers.stream() .map(ClubMember::getId) .collect(Collectors.toSet()); - clubMemberService.saveAll(filterCreatedMembers(updatedClubMembers, updatedMemberIds, currentMemberIds)); - clubMemberService.deleteAll(filterDeletedMembers(clubMembers, updatedMemberIds, currentMemberIds)); + clubMemberService.saveAll(filterCreatedMembers(updateClubMemberInfos, updatedMemberInfoIds, currentMemberIds)); + clubMemberService.updateAll(filterUpdatedMembers(updateClubMemberInfos, updatedMemberInfoIds, currentMemberIds)); + + List deletedMembers = filterDeletedMembers(clubMembers, updatedMemberInfoIds, currentMemberIds); + club.removeAll(deletedMembers); + clubMemberService.deleteAll(deletedMembers); } @Override @@ -91,10 +95,20 @@ private List filterCreatedMembers(List updatedClubMember .toList(); } + private List filterUpdatedMembers(List updatedClubMembers, Set updatedMemberIds, + Set currentMemberIds) { + Set willUpdateMemberIds = new HashSet<>(currentMemberIds); + willUpdateMemberIds.retainAll(updatedMemberIds); + return updatedClubMembers.stream() + .filter(member -> willUpdateMemberIds.contains(member.getId())) + .toList(); + } + private List filterDeletedMembers(List clubMembers, Set updatedMemberIds, Set currentMemberIds) { Set deletedMemberIds = new HashSet<>(currentMemberIds); deletedMemberIds.removeAll(updatedMemberIds); + System.out.println(deletedMemberIds); return clubMembers.stream() .filter(member -> deletedMemberIds.contains(member.getId())) .toList(); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/GeneralClubMemberService.java b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/GeneralClubMemberService.java index c9de3208b..f0d2b04d3 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/GeneralClubMemberService.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/clubmember/service/GeneralClubMemberService.java @@ -4,6 +4,9 @@ import ddingdong.ddingdongBE.domain.clubmember.entity.ClubMember; import ddingdong.ddingdongBE.domain.clubmember.repository.ClubMemberRepository; import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,7 +33,7 @@ public void saveAll(List clubMembers) { @Override @Transactional public void deleteAll(List clubMembers) { - clubMemberRepository.deleteAllInBatch(clubMembers); + clubMemberRepository.deleteAll(clubMembers); } @Override @@ -44,4 +47,20 @@ public void delete(ClubMember clubMember) { public void save(ClubMember clubMember) { clubMemberRepository.save(clubMember); } + + @Override + @Transactional + public void updateAll(final List updateClubMemberInfos) { + Map updatedMemberMap = updateClubMemberInfos.stream() + .collect(Collectors.toMap(ClubMember::getId, Function.identity())); + + clubMemberRepository.findByIdIn(updatedMemberMap.keySet()) + .forEach(member -> + member.updateInformation(updatedMemberMap.get(member.getId()))); + } + + @Override + public List getByClubId(final Long clubId) { + return clubMemberRepository.findByClubId(clubId); + } } diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java b/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java index 732bc3978..44f778c59 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/entity/Form.java @@ -14,6 +14,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -23,10 +24,14 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter +@SQLDelete(sql = "update form set deleted_at = CURRENT_TIMESTAMP where id=?") +@SQLRestriction("deleted_at IS NULL") public class Form extends BaseEntity { @Id @@ -54,6 +59,9 @@ public class Form extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) private Club club; + @Column(columnDefinition = "TIMESTAMP") + private LocalDateTime deletedAt; + @OneToMany(mappedBy = "form", cascade = CascadeType.ALL, orphanRemoval = true) private List formFields = new ArrayList<>(); diff --git a/src/main/java/ddingdong/ddingdongBE/domain/form/entity/FormField.java b/src/main/java/ddingdong/ddingdongBE/domain/form/entity/FormField.java index b3c93065f..de1d71b4e 100644 --- a/src/main/java/ddingdong/ddingdongBE/domain/form/entity/FormField.java +++ b/src/main/java/ddingdong/ddingdongBE/domain/form/entity/FormField.java @@ -12,15 +12,20 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; +import java.time.LocalDateTime; import java.util.List; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter +@SQLDelete(sql = "update form_field set deleted_at = CURRENT_TIMESTAMP where id=?") +@SQLRestriction("deleted_at IS NULL") public class FormField extends BaseEntity { @Id @@ -49,6 +54,9 @@ public class FormField extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) private Form form; + @Column(columnDefinition = "TIMESTAMP") + private LocalDateTime deletedAt; + @Builder private FormField( Long id, diff --git a/src/main/resources/db/migration/V45__add_form_deleted_at.sql b/src/main/resources/db/migration/V45__add_form_deleted_at.sql new file mode 100644 index 000000000..ddbab7097 --- /dev/null +++ b/src/main/resources/db/migration/V45__add_form_deleted_at.sql @@ -0,0 +1,5 @@ +ALTER TABLE form + ADD deleted_at timestamp NULL; + +ALTER TABLE form_field + ADD deleted_at timestamp NULL; diff --git a/src/test/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceTest.java b/src/test/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceTest.java index b83b8a932..9212b6c95 100644 --- a/src/test/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceTest.java +++ b/src/test/java/ddingdong/ddingdongBE/domain/clubmember/service/FacadeCentralClubMemberServiceTest.java @@ -62,8 +62,21 @@ class FacadeCentralClubMemberServiceTest extends TestContainerSupport { @DisplayName("엑셀 파일을 통해 동아리원 명단을 수정한다.") @Test - void updateClubMemberList() throws IOException { - //given + void updateAllClubMemberList() throws IOException { + // given + User user = UserFixture.createClubUser(); + User savedUser = userRepository.save(user); + Club club = ClubFixture.createClub(savedUser); + Club savedClub = clubRepository.save(club); + + ClubMember member1 = ClubMember.builder().club(savedClub).name("기존멤버1").build(); + ClubMember member2 = ClubMember.builder().club(savedClub).name("기존멤버2").build(); + ClubMember member3 = ClubMember.builder().club(savedClub).name("기존멤버3").build(); + + List existingMembers = clubMemberRepository.saveAll(List.of(member1, member2, member3)); + club.addClubMembers(existingMembers); + + // 엑셀 파일 생성 (기존 멤버 중 1, 2번만 유지하고 3번 삭제) ByteArrayOutputStream out = new ByteArrayOutputStream(); Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("Members"); @@ -75,21 +88,24 @@ void updateClubMemberList() throws IOException { header.createCell(4).setCellValue("비교(임원진) - 영어만"); header.createCell(5).setCellValue("학과(부)"); + // 기존 멤버 1번만 유지 Row row1 = sheet.createRow(1); - row1.createCell(0).setCellValue(1); - row1.createCell(1).setCellValue("5uhwann"); + row1.createCell(0).setCellValue(existingMembers.get(0).getId()); + row1.createCell(1).setCellValue("수정된멤버1"); row1.createCell(2).setCellValue("60001234"); row1.createCell(3).setCellValue("010-1234-5678"); - row1.createCell(4).setCellValue("LEADER"); - row1.createCell(5).setCellValue("융합소프트웨어학부"); + row1.createCell(4).setCellValue("MEMBER"); + row1.createCell(5).setCellValue("컴퓨터공학과"); + // 기존 멤버 2번만 유지 Row row2 = sheet.createRow(2); - row2.createCell(0).setCellValue(6); - row2.createCell(1).setCellValue("5uhwann"); - row2.createCell(2).setCellValue(60001234); - row2.createCell(3).setCellValue("010-1234-5678"); - row2.createCell(4).setCellValue("LEADER"); - row2.createCell(5).setCellValue("융합소프트웨어학부"); + row2.createCell(0).setCellValue(existingMembers.get(1).getId()); + row2.createCell(1).setCellValue("수정된멤버2"); + row2.createCell(2).setCellValue("60002345"); + row2.createCell(3).setCellValue("010-2345-6789"); + row2.createCell(4).setCellValue("MEMBER"); + row2.createCell(5).setCellValue("컴퓨터공학과"); + workbook.write(out); workbook.close(); @@ -101,37 +117,25 @@ void updateClubMemberList() throws IOException { in ); - User savedUser = userRepository.save(fixtureMonkey.giveMeOne(User.class)); - Club savedClub = clubRepository.save(fixtureMonkey.giveMeBuilder(Club.class) - .set("user", savedUser) - .set("score", Score.from(BigDecimal.ZERO)) - .set("clubMembers", List.of()) - .set("deletedAt", null) - .sample()); - List clubMembers = fixtureMonkey.giveMeBuilder(ClubMember.class) - .set("club", savedClub) - .set("deletedAt", null) - .sampleList(5); - clubMemberRepository.saveAll(clubMembers); - entityManager.flush(); - entityManager.clear(); - UpdateClubMemberListCommand command = UpdateClubMemberListCommand.builder() .userId(savedUser.getId()) .clubMemberListFile(validExcelFile) .build(); - //when + // when facadeCentralClubMemberService.updateMemberList(command); - //then - List updatedClubMemberList = clubMemberRepository.findAll(); - assertThat(updatedClubMemberList.size()).isEqualTo(2); + // then + List remainingMembers = clubMemberRepository.findAll(); + assertThat(remainingMembers).hasSize(2); // member3는 soft delete됨 + assertThat(remainingMembers) + .extracting(ClubMember::getName) + .containsExactlyInAnyOrder("수정된멤버1", "수정된멤버2"); } @DisplayName("동아리원 정보를 수정한다.") @Test - void update() { + void updateAll() { //given User savedUser = userRepository.save(fixtureMonkey.giveMeOne(User.class)); Club savedClub = clubRepository.save(fixtureMonkey.giveMeBuilder(Club.class)