From 46616d17d36b738df85d95c2d61c54cdd6c8bb13 Mon Sep 17 00:00:00 2001 From: min429 Date: Sat, 5 Oct 2024 23:06:48 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=EB=B0=B1=EB=AC=B8=EB=B0=B1?= =?UTF-8?q?=EB=8B=B5=20=EB=AA=A8=EB=91=90=20=EC=A0=80=EC=9E=A5(=EA=B5=90?= =?UTF-8?q?=EC=B2=B4)=20API=EB=A5=BC=20=EA=B5=AC=ED=98=84=ED=95=9C?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/qna100/api/QnaController.java | 37 ++++++++++++++++++ .../qna100/api/dto/CreateQnaRequest.java | 28 ++++++++++++++ .../accompany/domain/qna100/entity/Qna.java | 38 +++++++++++++++++++ .../qna100/infrastructure/QnaRepository.java | 12 ++++++ .../querydsl/QnaRepositoryImpl.java | 18 +++++++++ .../interfaces/QnaRepositoryCustom.java | 5 +++ .../domain/qna100/service/QnaService.java | 26 +++++++++++++ 7 files changed, 164 insertions(+) create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/entity/Qna.java create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/QnaRepositoryImpl.java create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/interfaces/QnaRepositoryCustom.java create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java new file mode 100644 index 0000000..764c8fc --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java @@ -0,0 +1,37 @@ +package com.dnd.accompany.domain.qna100.api; + +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.dnd.accompany.domain.auth.dto.jwt.JwtAuthentication; +import com.dnd.accompany.domain.qna100.api.dto.CreateQnaRequest; +import com.dnd.accompany.domain.qna100.service.QnaService; +import com.dnd.accompany.domain.user.entity.User; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@Tag(name = "QnA 100") +@RequiredArgsConstructor +@RequestMapping("api/v1/qnas") +@RestController +public class QnaController { + + private final QnaService qnaService; + + @Operation(summary = "백문백답 모두 저장(교체)") + @PutMapping + public ResponseEntity saveAll( + @RequestBody @Valid CreateQnaRequest request, + @AuthenticationPrincipal JwtAuthentication user) { + qnaService.saveAll(user.getId(), request); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java new file mode 100644 index 0000000..d1c0caa --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java @@ -0,0 +1,28 @@ +package com.dnd.accompany.domain.qna100.api.dto; + +import static java.util.stream.Collectors.*; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.dnd.accompany.domain.qna100.entity.Qna; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record CreateQnaRequest( + List<@Size(max = 2000) String> questions, + List<@Size(max = 2000) String> answers +) { + + public List toEntityList(Long userId) { + return IntStream.range(0, questions.size()) + .mapToObj(i -> Qna.builder() + .userId(userId) + .question(questions.get(i)) + .answer(answers.get(i)) + .build()) + .collect(toList()); + } +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna.java b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna.java new file mode 100644 index 0000000..b395369 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna.java @@ -0,0 +1,38 @@ +package com.dnd.accompany.domain.qna100.entity; + +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "qnas") +public class Qna { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long userId; + + @Column(length = 2000) + private String question; + + @Column(length = 2000) + private String answer; +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java new file mode 100644 index 0000000..86a3da1 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java @@ -0,0 +1,12 @@ +package com.dnd.accompany.domain.qna100.infrastructure; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.dnd.accompany.domain.qna100.entity.Qna; +import com.dnd.accompany.domain.qna100.infrastructure.querydsl.interfaces.QnaRepositoryCustom; + +@Repository +public interface QnaRepository extends JpaRepository, QnaRepositoryCustom { + void deleteAllByUserId(Long userId); +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/QnaRepositoryImpl.java b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/QnaRepositoryImpl.java new file mode 100644 index 0000000..c1363c5 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/QnaRepositoryImpl.java @@ -0,0 +1,18 @@ +package com.dnd.accompany.domain.qna100.infrastructure.querydsl; + +import org.springframework.stereotype.Repository; + +import com.dnd.accompany.domain.qna100.infrastructure.querydsl.interfaces.QnaRepositoryCustom; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class QnaRepositoryImpl implements QnaRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + + +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/interfaces/QnaRepositoryCustom.java b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/interfaces/QnaRepositoryCustom.java new file mode 100644 index 0000000..eed4772 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/querydsl/interfaces/QnaRepositoryCustom.java @@ -0,0 +1,5 @@ +package com.dnd.accompany.domain.qna100.infrastructure.querydsl.interfaces; + +public interface QnaRepositoryCustom { + +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java new file mode 100644 index 0000000..5ab5683 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java @@ -0,0 +1,26 @@ +package com.dnd.accompany.domain.qna100.service; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.dnd.accompany.domain.qna100.api.dto.CreateQnaRequest; +import com.dnd.accompany.domain.qna100.entity.Qna; +import com.dnd.accompany.domain.qna100.infrastructure.QnaRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class QnaService { + + private final QnaRepository qnaRepository; + + @Transactional + public void saveAll(Long userId, CreateQnaRequest request){ + List qnas = request.toEntityList(userId); + qnaRepository.deleteAllByUserId(userId); + qnaRepository.saveAll(qnas); + } +} From b686e7336b2635ea8ca684f90de13ba32f0420fc Mon Sep 17 00:00:00 2001 From: min429 Date: Sun, 6 Oct 2024 15:43:24 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20dto=20=ED=95=84=EB=93=9C=EB=A5=BC?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/qna100/api/QnaController.java | 4 +--- .../domain/qna100/api/dto/CreateQnaRequest.java | 16 +++++++--------- .../dnd/accompany/domain/qna100/api/dto/Qna.java | 9 +++++++++ .../qna100/entity/{Qna.java => Qna100.java} | 7 ++----- .../qna100/infrastructure/QnaRepository.java | 4 ++-- .../domain/qna100/service/QnaService.java | 6 +++--- 6 files changed, 24 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java rename src/main/java/com/dnd/accompany/domain/qna100/entity/{Qna.java => Qna100.java} (85%) diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java index 764c8fc..35f3ced 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java @@ -1,6 +1,5 @@ package com.dnd.accompany.domain.qna100.api; -import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PutMapping; @@ -11,7 +10,6 @@ import com.dnd.accompany.domain.auth.dto.jwt.JwtAuthentication; import com.dnd.accompany.domain.qna100.api.dto.CreateQnaRequest; import com.dnd.accompany.domain.qna100.service.QnaService; -import com.dnd.accompany.domain.user.entity.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -20,7 +18,7 @@ @Tag(name = "QnA 100") @RequiredArgsConstructor -@RequestMapping("api/v1/qnas") +@RequestMapping("api/v1/qna100s") @RestController public class QnaController { diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java index d1c0caa..b31e242 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java @@ -6,22 +6,20 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import com.dnd.accompany.domain.qna100.entity.Qna; +import com.dnd.accompany.domain.qna100.entity.Qna100; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; public record CreateQnaRequest( - List<@Size(max = 2000) String> questions, - List<@Size(max = 2000) String> answers + @NotNull List qnas ) { - public List toEntityList(Long userId) { - return IntStream.range(0, questions.size()) - .mapToObj(i -> Qna.builder() + public List toEntityList(Long userId, List qnas) { + return qnas.stream() + .map(qna -> Qna100.builder() .userId(userId) - .question(questions.get(i)) - .answer(answers.get(i)) + .question(qna.questions()) + .answer(qna.answers()) .build()) .collect(toList()); } diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java new file mode 100644 index 0000000..9fdfdb0 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java @@ -0,0 +1,9 @@ +package com.dnd.accompany.domain.qna100.api.dto; + +import jakarta.validation.constraints.Size; + +public record Qna( + @Size(max = 2000) String questions, + @Size(max = 2000) String answers +) { +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna.java b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java similarity index 85% rename from src/main/java/com/dnd/accompany/domain/qna100/entity/Qna.java rename to src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java index b395369..3b6b021 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java @@ -1,8 +1,5 @@ package com.dnd.accompany.domain.qna100.entity; -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.SQLRestriction; - import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -20,8 +17,8 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PROTECTED) @Entity -@Table(name = "qnas") -public class Qna { +@Table(name = "qna100s") +public class Qna100 { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java index 86a3da1..d137efc 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java @@ -3,10 +3,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import com.dnd.accompany.domain.qna100.entity.Qna; +import com.dnd.accompany.domain.qna100.entity.Qna100; import com.dnd.accompany.domain.qna100.infrastructure.querydsl.interfaces.QnaRepositoryCustom; @Repository -public interface QnaRepository extends JpaRepository, QnaRepositoryCustom { +public interface QnaRepository extends JpaRepository, QnaRepositoryCustom { void deleteAllByUserId(Long userId); } diff --git a/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java index 5ab5683..611df27 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java @@ -6,7 +6,7 @@ import org.springframework.transaction.annotation.Transactional; import com.dnd.accompany.domain.qna100.api.dto.CreateQnaRequest; -import com.dnd.accompany.domain.qna100.entity.Qna; +import com.dnd.accompany.domain.qna100.entity.Qna100; import com.dnd.accompany.domain.qna100.infrastructure.QnaRepository; import lombok.RequiredArgsConstructor; @@ -19,8 +19,8 @@ public class QnaService { @Transactional public void saveAll(Long userId, CreateQnaRequest request){ - List qnas = request.toEntityList(userId); + List qna100s = request.toEntityList(userId, request.qnas()); qnaRepository.deleteAllByUserId(userId); - qnaRepository.saveAll(qnas); + qnaRepository.saveAll(qna100s); } } From 30cb31cab7f9660b92cd07781c2d1188b75cf1e6 Mon Sep 17 00:00:00 2001 From: min429 Date: Sun, 13 Oct 2024 15:46:50 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20'=EB=B0=B1=EB=AC=B8=EB=B0=B1?= =?UTF-8?q?=EB=8B=B5=20=EB=AA=A8=EB=91=90=20=EC=A0=80=EC=9E=A5(=EA=B5=90?= =?UTF-8?q?=EC=B2=B4)'=20API=EB=A5=BC=20'=EB=B0=B1=EB=AC=B8=EB=B0=B1?= =?UTF-8?q?=EB=8B=B5=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=88=98=EC=A0=95?= =?UTF-8?q?'=20API=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/qna100/api/QnaController.java | 14 +++++----- ...st.java => CreateAndUpdateQnaRequest.java} | 13 ++++----- .../accompany/domain/qna100/api/dto/Qna.java | 2 ++ .../domain/qna100/entity/Qna100.java | 8 ++++++ .../domain/qna100/service/QnaService.java | 28 +++++++++++++++---- 5 files changed, 46 insertions(+), 19 deletions(-) rename src/main/java/com/dnd/accompany/domain/qna100/api/dto/{CreateQnaRequest.java => CreateAndUpdateQnaRequest.java} (61%) diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java index 35f3ced..0482e12 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java @@ -2,13 +2,13 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.dnd.accompany.domain.auth.dto.jwt.JwtAuthentication; -import com.dnd.accompany.domain.qna100.api.dto.CreateQnaRequest; +import com.dnd.accompany.domain.qna100.api.dto.CreateAndUpdateQnaRequest; import com.dnd.accompany.domain.qna100.service.QnaService; import io.swagger.v3.oas.annotations.Operation; @@ -24,12 +24,12 @@ public class QnaController { private final QnaService qnaService; - @Operation(summary = "백문백답 모두 저장(교체)") - @PutMapping - public ResponseEntity saveAll( - @RequestBody @Valid CreateQnaRequest request, + @Operation(summary = "백문백답 추가 및 수정") + @PostMapping("create-update") + public ResponseEntity createAndUpdate( + @RequestBody @Valid CreateAndUpdateQnaRequest request, @AuthenticationPrincipal JwtAuthentication user) { - qnaService.saveAll(user.getId(), request); + qnaService.saveAndUpdate(user.getId(), request); return ResponseEntity.ok().build(); } } diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateAndUpdateQnaRequest.java similarity index 61% rename from src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java rename to src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateAndUpdateQnaRequest.java index b31e242..6158409 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateQnaRequest.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/CreateAndUpdateQnaRequest.java @@ -3,24 +3,23 @@ import static java.util.stream.Collectors.*; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.IntStream; import com.dnd.accompany.domain.qna100.entity.Qna100; -import jakarta.validation.constraints.NotNull; - -public record CreateQnaRequest( - @NotNull List qnas +public record CreateAndUpdateQnaRequest( + List qnas ) { - public List toEntityList(Long userId, List qnas) { + public Map> toMap(Long userId, List qnas) { return qnas.stream() .map(qna -> Qna100.builder() + .id(qna.id()) .userId(userId) .question(qna.questions()) .answer(qna.answers()) .build()) - .collect(toList()); + .collect(partitioningBy(qna -> qna.getId() == null)); } } diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java index 9fdfdb0..65fd5f8 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/Qna.java @@ -1,8 +1,10 @@ package com.dnd.accompany.domain.qna100.api.dto; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; public record Qna( + Long id, @Size(max = 2000) String questions, @Size(max = 2000) String answers ) { diff --git a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java index 3b6b021..0fbe994 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java @@ -32,4 +32,12 @@ public class Qna100 { @Column(length = 2000) private String answer; + + public void setQuestion(String question) { + this.question = question; + } + + public void setAnswer(String answer) { + this.answer = answer; + } } diff --git a/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java index 611df27..1551a1e 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java @@ -1,11 +1,15 @@ package com.dnd.accompany.domain.qna100.service; +import static java.util.stream.Collectors.*; + import java.util.List; +import java.util.Map; +import java.util.function.Function; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.dnd.accompany.domain.qna100.api.dto.CreateQnaRequest; +import com.dnd.accompany.domain.qna100.api.dto.CreateAndUpdateQnaRequest; import com.dnd.accompany.domain.qna100.entity.Qna100; import com.dnd.accompany.domain.qna100.infrastructure.QnaRepository; @@ -18,9 +22,23 @@ public class QnaService { private final QnaRepository qnaRepository; @Transactional - public void saveAll(Long userId, CreateQnaRequest request){ - List qna100s = request.toEntityList(userId, request.qnas()); - qnaRepository.deleteAllByUserId(userId); - qnaRepository.saveAll(qna100s); + public void saveAndUpdate(Long userId, CreateAndUpdateQnaRequest request) { + Map> qnaMap = request.toMap(userId, request.qnas()); + + List newQnas = qnaMap.get(true); + List existingQnas = qnaMap.get(false); + + Map existingQnaMap = existingQnas.stream() + .collect(toMap(Qna100::getId, Function.identity())); + + List qnas = qnaRepository.findAllById(existingQnaMap.keySet()); + + qnas.forEach(qna -> { + Long qnaId = qna.getId(); + qna.setQuestion(existingQnaMap.get(qnaId).getQuestion()); + qna.setAnswer(existingQnaMap.get(qnaId).getAnswer()); + }); + + qnaRepository.saveAll(newQnas); } } From d3dfb87f5dda5cdb7b6b12b297a592c22c65ff3e Mon Sep 17 00:00:00 2001 From: min429 Date: Sun, 13 Oct 2024 15:55:10 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20@softDelete=EB=A5=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/accompany/domain/qna100/entity/Qna100.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java index 0fbe994..06c9b8e 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java @@ -1,5 +1,7 @@ package com.dnd.accompany.domain.qna100.entity; +import org.hibernate.annotations.SQLDelete; + import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -18,6 +20,7 @@ @AllArgsConstructor(access = AccessLevel.PROTECTED) @Entity @Table(name = "qna100s") +@SQLDelete(sql = "UPDATE qna100s SET deleted = true WHERE id = ?") public class Qna100 { @Id From 1d2f02f1cfc83d7db5c605a565c7ab8349fc723f Mon Sep 17 00:00:00 2001 From: min429 Date: Sun, 13 Oct 2024 16:01:21 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=EB=88=84=EB=9D=BD=EB=90=9C=20delet?= =?UTF-8?q?ed=20=ED=95=84=EB=93=9C=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=9C?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/accompany/domain/qna100/entity/Qna100.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java index 06c9b8e..838b05c 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/entity/Qna100.java @@ -36,6 +36,8 @@ public class Qna100 { @Column(length = 2000) private String answer; + private boolean deleted = false; + public void setQuestion(String question) { this.question = question; } From e76cef70882e59ea001f1ff6992dc09e38ee8581 Mon Sep 17 00:00:00 2001 From: min429 Date: Sun, 13 Oct 2024 16:35:45 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=EB=B0=B1=EB=AC=B8=EB=B0=B1?= =?UTF-8?q?=EB=8B=B5=20=EA=B4=80=EB=A0=A8=20exception=EC=9D=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qna100/exception/Qna100AccessDeniedException.java | 10 ++++++++++ .../qna100/exception/Qna100NotFoundException.java | 10 ++++++++++ .../accompany/global/common/response/ErrorCode.java | 4 ++++ 3 files changed, 24 insertions(+) create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100AccessDeniedException.java create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100NotFoundException.java diff --git a/src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100AccessDeniedException.java b/src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100AccessDeniedException.java new file mode 100644 index 0000000..4b283f5 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100AccessDeniedException.java @@ -0,0 +1,10 @@ +package com.dnd.accompany.domain.qna100.exception; + +import com.dnd.accompany.global.common.exception.BusinessException; +import com.dnd.accompany.global.common.response.ErrorCode; + +public class Qna100AccessDeniedException extends BusinessException { + public Qna100AccessDeniedException(ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100NotFoundException.java b/src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100NotFoundException.java new file mode 100644 index 0000000..ea431e6 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/exception/Qna100NotFoundException.java @@ -0,0 +1,10 @@ +package com.dnd.accompany.domain.qna100.exception; + +import com.dnd.accompany.global.common.exception.BusinessException; +import com.dnd.accompany.global.common.response.ErrorCode; + +public class Qna100NotFoundException extends BusinessException { + public Qna100NotFoundException(ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/com/dnd/accompany/global/common/response/ErrorCode.java b/src/main/java/com/dnd/accompany/global/common/response/ErrorCode.java index f2e1cf0..ff8638f 100644 --- a/src/main/java/com/dnd/accompany/global/common/response/ErrorCode.java +++ b/src/main/java/com/dnd/accompany/global/common/response/ErrorCode.java @@ -38,6 +38,10 @@ public enum ErrorCode { // ---- 동행 신청서 ---- // ACCOMPANY_REQUEST_NOT_FOUND(MatripConstant.NOT_FOUND, "ACCOMPANY_REQUEST-001", "동행 신청서를 찾을 수 없습니다."), + // ---- 백문백답 ---- // + QNA100_NOT_FOUND(MatripConstant.NOT_FOUND, "QNA100-001", "백문백답을 찾을 수 없습니다."), + QNA100_ACCESS_DENIED(MatripConstant.FORBIDDEN, "QNA100-002", "백문백답 접근 권한이 없습니다."), + // ---- 이미지 ---- // IMAGE_NOT_EXISTS(MatripConstant.NOT_FOUND, "IMAGE-001", "이미지를 찾을 수 없습니다."), FILE_IO_EXCEPTION(MatripConstant.INTERNAL_SERVER_ERROR, "IMAGE-002", "파일 생성에 실패했습니다."), From 181ec875b2a4bc5ff30f2a638f22a95a5b0cd430 Mon Sep 17 00:00:00 2001 From: min429 Date: Sun, 13 Oct 2024 16:36:10 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20=EB=B0=B1=EB=AC=B8=EB=B0=B1?= =?UTF-8?q?=EB=8B=B5=20=EC=82=AD=EC=A0=9C=20API=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/qna100/api/QnaController.java | 11 +++++++++++ .../domain/qna100/api/dto/DeleteQnaRequest.java | 8 ++++++++ .../qna100/infrastructure/QnaRepository.java | 6 +++++- .../domain/qna100/service/QnaService.java | 17 +++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/dnd/accompany/domain/qna100/api/dto/DeleteQnaRequest.java diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java index 0482e12..9b7bda0 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/QnaController.java @@ -2,6 +2,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -9,6 +10,7 @@ import com.dnd.accompany.domain.auth.dto.jwt.JwtAuthentication; import com.dnd.accompany.domain.qna100.api.dto.CreateAndUpdateQnaRequest; +import com.dnd.accompany.domain.qna100.api.dto.DeleteQnaRequest; import com.dnd.accompany.domain.qna100.service.QnaService; import io.swagger.v3.oas.annotations.Operation; @@ -32,4 +34,13 @@ public ResponseEntity createAndUpdate( qnaService.saveAndUpdate(user.getId(), request); return ResponseEntity.ok().build(); } + + @Operation(summary = "백문백답 삭제") + @DeleteMapping + public ResponseEntity delete( + @RequestBody @Valid DeleteQnaRequest request, + @AuthenticationPrincipal JwtAuthentication user) { + qnaService.delete(user.getId(), request); + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/com/dnd/accompany/domain/qna100/api/dto/DeleteQnaRequest.java b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/DeleteQnaRequest.java new file mode 100644 index 0000000..43a93c6 --- /dev/null +++ b/src/main/java/com/dnd/accompany/domain/qna100/api/dto/DeleteQnaRequest.java @@ -0,0 +1,8 @@ +package com.dnd.accompany.domain.qna100.api.dto; + +import java.util.List; + +public record DeleteQnaRequest( + List ids +) { +} diff --git a/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java index d137efc..09bcc1c 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/infrastructure/QnaRepository.java @@ -1,5 +1,8 @@ package com.dnd.accompany.domain.qna100.infrastructure; +import java.util.List; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -8,5 +11,6 @@ @Repository public interface QnaRepository extends JpaRepository, QnaRepositoryCustom { - void deleteAllByUserId(Long userId); + Optional findFirstByUserId(Long userId); + void deleteByIdIn(List ids); } diff --git a/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java index 1551a1e..7ef795b 100644 --- a/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java +++ b/src/main/java/com/dnd/accompany/domain/qna100/service/QnaService.java @@ -6,12 +6,17 @@ import java.util.Map; import java.util.function.Function; +import org.springframework.data.domain.Example; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.dnd.accompany.domain.qna100.api.dto.CreateAndUpdateQnaRequest; +import com.dnd.accompany.domain.qna100.api.dto.DeleteQnaRequest; import com.dnd.accompany.domain.qna100.entity.Qna100; +import com.dnd.accompany.domain.qna100.exception.Qna100AccessDeniedException; +import com.dnd.accompany.domain.qna100.exception.Qna100NotFoundException; import com.dnd.accompany.domain.qna100.infrastructure.QnaRepository; +import com.dnd.accompany.global.common.response.ErrorCode; import lombok.RequiredArgsConstructor; @@ -41,4 +46,16 @@ public void saveAndUpdate(Long userId, CreateAndUpdateQnaRequest request) { qnaRepository.saveAll(newQnas); } + + @Transactional + public void delete(Long userId, DeleteQnaRequest request) { + Qna100 qna = qnaRepository.findFirstByUserId(userId) + .orElseThrow(() -> new Qna100NotFoundException(ErrorCode.QNA100_NOT_FOUND)); + + if(!userId.equals(qna.getUserId())){ + throw new Qna100AccessDeniedException(ErrorCode.QNA100_ACCESS_DENIED); + } + + qnaRepository.deleteByIdIn(request.ids()); + } }