Skip to content

Commit

Permalink
[DEV-13] FAQ API 구현 (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
5uhwann authored Jul 26, 2024
1 parent 5988c87 commit e97fe04
Show file tree
Hide file tree
Showing 27 changed files with 674 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthService authSer
API_PREFIX + "/clubs/**",
API_PREFIX + "/notices/**",
API_PREFIX + "/banners/**",
API_PREFIX + "/documents/**")
API_PREFIX + "/documents/**",
API_PREFIX + "/questions/**")
.permitAll()
.antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**").permitAll()
.anyRequest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
@Getter
@RequiredArgsConstructor
public enum ErrorMessage {
AUTHENTICATION_ERROR("알 수 없는 인증 관련 오류가 발생하였습니다."),
INTERNAL_SERVER_ERROR("서버에 문제가 발생했습니다."),
ILLEGAL_CLUB_LOCATION_PATTERN("올바르지 않은 동아리 위치 양식입니다."),
ILLEGAL_CLUB_PHONE_NUMBER_PATTERN("올바르지 않은 동아리 전화번호 양식입니다."),
Expand All @@ -26,7 +25,8 @@ public enum ErrorMessage {
NO_SUCH_BANNER("해당 배너가 존재하지 않습니다."),
NO_SUCH_FIX("해당 수리 신청서가 존재하지 않습니다."),
NO_SUCH_FIX_ZONE_COMMENT("존재하지 않는 픽스존 댓글입니다."),
NO_SUCH_DOCUMENT("해당 자료가 존재하지 않습니다.");
NO_SUCH_DOCUMENT("해당 자료가 존재하지 않습니다."),
NO_SUCH_QUESTION("해당 질문이 존재하지 않습니다.");

private final String text;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ddingdong.ddingdongBE.domain.documents.api;


import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.GenerateDocumentRequest;
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.ModifyDocumentRequest;
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.AdminDetailDocumentResponse;
Expand All @@ -11,6 +12,7 @@
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
Expand All @@ -30,8 +32,10 @@ public interface AdminDocumentApi {
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.CREATED)
@SecurityRequirement(name = "AccessToken")
void generateDocument(@ModelAttribute GenerateDocumentRequest generateDocumentRequest,
@RequestPart(name = "uploadFiles") List<MultipartFile> uploadFiles);
void generateDocument(
@AuthenticationPrincipal PrincipalDetails principalDetails,
@ModelAttribute GenerateDocumentRequest generateDocumentRequest,
@RequestPart(name = "uploadFiles") List<MultipartFile> uploadFiles);

@Operation(summary = "어드민 자료실 목록 조회 API")
@GetMapping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@ public interface DocumentApi {
@Operation(summary = "자료실 목록 조회 API")
@GetMapping
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
List<DocumentResponse> getAllDocuments();

@Operation(summary = "자료실 상세 조회 API")
@GetMapping("/{documentId}")
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
DetailDocumentResponse getDetailDocument(@PathVariable Long documentId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static ddingdong.ddingdongBE.domain.fileinformation.entity.FileDomainCategory.DOCUMENT;
import static ddingdong.ddingdongBE.domain.fileinformation.entity.FileTypeCategory.FILE;

import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.documents.api.AdminDocumentApi;
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.GenerateDocumentRequest;
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.ModifyDocumentRequest;
Expand All @@ -11,10 +12,12 @@
import ddingdong.ddingdongBE.domain.documents.entity.Document;
import ddingdong.ddingdongBE.domain.documents.service.DocumentService;
import ddingdong.ddingdongBE.domain.fileinformation.service.FileInformationService;
import ddingdong.ddingdongBE.domain.user.entity.User;
import ddingdong.ddingdongBE.file.dto.FileResponse;
import ddingdong.ddingdongBE.file.service.FileService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestPart;
Expand All @@ -29,9 +32,12 @@ public class AdminDocumentController implements AdminDocumentApi {
private final FileService fileService;
private final FileInformationService fileInformationService;

public void generateDocument(@ModelAttribute GenerateDocumentRequest generateDocumentRequest,
@RequestPart(name = "uploadFiles") List<MultipartFile> uploadFiles) {
Long createdDocumentId = documentService.create(generateDocumentRequest.toEntity());
public void generateDocument(
@AuthenticationPrincipal PrincipalDetails principalDetails,
@ModelAttribute GenerateDocumentRequest generateDocumentRequest,
@RequestPart(name = "uploadFiles") List<MultipartFile> uploadFiles) {
User admin = principalDetails.getUser();
Long createdDocumentId = documentService.create(generateDocumentRequest.toEntity(admin));
fileService.uploadDownloadableFile(createdDocumentId, uploadFiles, FILE, DOCUMENT);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package ddingdong.ddingdongBE.domain.documents.controller.dto.request;

import ddingdong.ddingdongBE.domain.documents.entity.Document;
import ddingdong.ddingdongBE.domain.user.entity.User;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Schema(
name = "GenerateDocumentRequest",
Expand All @@ -18,8 +17,9 @@ public record GenerateDocumentRequest(
@Schema(description = "자료 내용", example = "내용")
String content
) {
public Document toEntity() {
public Document toEntity(User user) {
return Document.builder()
.user(user)
.title(title)
.content(content)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package ddingdong.ddingdongBE.domain.documents.entity;

import ddingdong.ddingdongBE.common.BaseEntity;
import ddingdong.ddingdongBE.domain.user.entity.User;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -21,15 +25,20 @@ public class Document extends BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

@Column(nullable = false)
private String title;

@Column(nullable = false, length = 1024)
private String content;

@Builder
private Document(Long id, String title, String content, LocalDateTime createdAt) {
private Document(Long id, User user, String title, String content, LocalDateTime createdAt) {
this.id = id;
this.user = user;
this.title = title;
this.content = content;
super.setCreatedAt(createdAt);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ddingdong.ddingdongBE.domain.question.api;


import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.question.controller.dto.request.GenerateQuestionRequest;
import ddingdong.ddingdongBE.domain.question.controller.dto.request.ModifyQuestionRequest;
import ddingdong.ddingdongBE.domain.question.controller.dto.response.AdminQuestionResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;

@Tag(name = "FAQ - Admin", description = "FAQ Admin API")
@RequestMapping("/server/admin/questions")
public interface AdminQuestionApi {

@Operation(summary = "어드민 FAQ 업로드 API")
@PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseStatus(HttpStatus.CREATED)
@SecurityRequirement(name = "AccessToken")
void generateQuestion(
@AuthenticationPrincipal PrincipalDetails principalDetails,
@ModelAttribute GenerateQuestionRequest generateDocumentRequest);

@Operation(summary = "어드민 FAQ 목록 조회 API")
@GetMapping
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
List<AdminQuestionResponse> getAllQuestions();

@Operation(summary = "어드민 FAQ 수정 API")
@PatchMapping(value = "/{questionId}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseStatus(HttpStatus.NO_CONTENT)
@SecurityRequirement(name = "AccessToken")
void modifyQuestion(@PathVariable Long questionId,
@ModelAttribute ModifyQuestionRequest modifyQuestionRequest);

@Operation(summary = "어드민 FAQ 삭제 API")
@DeleteMapping("/{questionId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@SecurityRequirement(name = "AccessToken")
void deleteQuestion(@PathVariable Long questionId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ddingdong.ddingdongBE.domain.question.api;


import ddingdong.ddingdongBE.domain.question.controller.dto.response.QuestionResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;

@Tag(name = "FAQ", description = "FAQ API")
@RequestMapping("/server/questions")
public interface QuestionApi {

@Operation(summary = "FAQ 목록 조회 API")
@GetMapping
@ResponseStatus(HttpStatus.OK)
List<QuestionResponse> getAllQuestions();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ddingdong.ddingdongBE.domain.question.controller;

import ddingdong.ddingdongBE.auth.PrincipalDetails;
import ddingdong.ddingdongBE.domain.question.api.AdminQuestionApi;
import ddingdong.ddingdongBE.domain.question.controller.dto.request.GenerateQuestionRequest;
import ddingdong.ddingdongBE.domain.question.controller.dto.request.ModifyQuestionRequest;
import ddingdong.ddingdongBE.domain.question.controller.dto.response.AdminQuestionResponse;
import ddingdong.ddingdongBE.domain.question.service.QuestionService;
import ddingdong.ddingdongBE.domain.user.entity.User;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class AdminQuestionController implements AdminQuestionApi {

private final QuestionService questionService;

@Override
public void generateQuestion(PrincipalDetails principalDetails, GenerateQuestionRequest generateDocumentRequest) {
User admin = principalDetails.getUser();
questionService.create(generateDocumentRequest.toEntity(admin));
}

@Override
public List<AdminQuestionResponse> getAllQuestions() {
return questionService.getAll().stream()
.map(AdminQuestionResponse::from)
.toList();
}

@Override
public void modifyQuestion(Long questionId, ModifyQuestionRequest modifyQuestionRequest) {
questionService.update(questionId, modifyQuestionRequest.toEntity());
}

@Override
public void deleteQuestion(Long questionId) {
questionService.delete(questionId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ddingdong.ddingdongBE.domain.question.controller;

import ddingdong.ddingdongBE.domain.question.api.QuestionApi;
import ddingdong.ddingdongBE.domain.question.controller.dto.response.QuestionResponse;
import ddingdong.ddingdongBE.domain.question.service.QuestionService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class QuestionController implements QuestionApi {

private final QuestionService questionService;

@Override
public List<QuestionResponse> getAllQuestions() {
return questionService.getAll().stream()
.map(QuestionResponse::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ddingdong.ddingdongBE.domain.question.controller.dto.request;

import ddingdong.ddingdongBE.domain.question.entity.Question;
import ddingdong.ddingdongBE.domain.user.entity.User;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Schema(
name = "GenerateQuestionRequest",
description = "FAQ 질문 생성 요청"
)
@Builder
public record GenerateQuestionRequest(
@Schema(description = "FAQ 질문", example = "질문")
String question,
@Schema(description = "FAQ 답변", example = "답변")
String reply
) {

public Question toEntity(User user) {
return Question.builder()
.user(user)
.question(this.question)
.reply(this.reply).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ddingdong.ddingdongBE.domain.question.controller.dto.request;

import ddingdong.ddingdongBE.domain.question.entity.Question;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Schema(
name = "ModifyQuestionRequest",
description = "FAQ 질문 수정 요청"
)
@Builder
public record ModifyQuestionRequest(
@Schema(description = "자료 제목", example = "제목")
String question,
@Schema(description = "자료 내용", example = "내용")
String reply
) {

public Question toEntity() {
return Question.builder()
.question(this.question)
.reply(this.reply)
.build();
}
}
Loading

0 comments on commit e97fe04

Please sign in to comment.