Skip to content

Commit 70ff3a8

Browse files
authored
[DEV-18] 자료실 API (#59)
1 parent 0f7f598 commit 70ff3a8

25 files changed

+865
-8
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dependencies {
5252
runtimeOnly 'com.h2database:h2'
5353
annotationProcessor 'org.projectlombok:lombok'
5454
testImplementation 'org.springframework.boot:spring-boot-starter-test'
55+
testImplementation 'org.springframework.security:spring-security-test'
5556
}
5657

5758
tasks.named('test') {

src/main/java/ddingdong/ddingdongBE/common/BaseEntity.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,11 @@ public abstract class BaseEntity {
2222
@Column(columnDefinition = "TIMESTAMP")
2323
private LocalDateTime updatedAt;
2424

25+
public void setCreatedAt(LocalDateTime createdAt) {
26+
this.createdAt = createdAt;
27+
}
28+
29+
public void setUpdatedAt(LocalDateTime updatedAt) {
30+
this.updatedAt = updatedAt;
31+
}
2532
}

src/main/java/ddingdong/ddingdongBE/common/config/SecurityConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthService authSer
3737
.antMatchers(GET,
3838
API_PREFIX + "/clubs/**",
3939
API_PREFIX + "/notices/**",
40-
API_PREFIX + "/banners/**")
40+
API_PREFIX + "/banners/**",
41+
API_PREFIX + "/documents/**")
4142
.permitAll()
4243
.antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**").permitAll()
4344
.anyRequest()

src/main/java/ddingdong/ddingdongBE/common/exception/ErrorMessage.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public enum ErrorMessage {
2323
UNREGISTER_ID("등록되지 않은 ID입니다."),
2424
NO_SUCH_BANNER("해당 배너가 존재하지 않습니다."),
2525
NON_VALIDATED_TOKEN("유효하지 않은 토큰입니다."),
26-
NO_SUCH_FIX("해당 수리 신청서가 존재하지 않습니다.");
26+
NO_SUCH_FIX("해당 수리 신청서가 존재하지 않습니다."),
27+
NO_SUCH_DOCUMENT("해당 자료가 존재하지 않습니다.");
2728

2829
private final String text;
2930
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package ddingdong.ddingdongBE.domain.documents.api;
2+
3+
4+
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.GenerateDocumentRequest;
5+
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.ModifyDocumentRequest;
6+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.AdminDetailDocumentResponse;
7+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.AdminDocumentResponse;
8+
import io.swagger.v3.oas.annotations.Operation;
9+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
10+
import io.swagger.v3.oas.annotations.tags.Tag;
11+
import java.util.List;
12+
import org.springframework.http.HttpStatus;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.web.bind.annotation.DeleteMapping;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.ModelAttribute;
17+
import org.springframework.web.bind.annotation.PatchMapping;
18+
import org.springframework.web.bind.annotation.PathVariable;
19+
import org.springframework.web.bind.annotation.PostMapping;
20+
import org.springframework.web.bind.annotation.RequestMapping;
21+
import org.springframework.web.bind.annotation.RequestPart;
22+
import org.springframework.web.bind.annotation.ResponseStatus;
23+
import org.springframework.web.multipart.MultipartFile;
24+
25+
@Tag(name = "Document - Admin", description = "Document Admin API")
26+
@RequestMapping("/server/admin/documents")
27+
public interface AdminDocumentApi {
28+
29+
@Operation(summary = "어드민 자료실 업로드 API")
30+
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
31+
@ResponseStatus(HttpStatus.CREATED)
32+
@SecurityRequirement(name = "AccessToken")
33+
void generateDocument(@ModelAttribute GenerateDocumentRequest generateDocumentRequest,
34+
@RequestPart(name = "uploadFiles") List<MultipartFile> uploadFiles);
35+
36+
@Operation(summary = "어드민 자료실 목록 조회 API")
37+
@GetMapping
38+
@ResponseStatus(HttpStatus.OK)
39+
@SecurityRequirement(name = "AccessToken")
40+
List<AdminDocumentResponse> getAllDocuments();
41+
42+
@Operation(summary = "어드민 자료실 상세 조회 API")
43+
@GetMapping("/{documentId}")
44+
@ResponseStatus(HttpStatus.OK)
45+
@SecurityRequirement(name = "AccessToken")
46+
AdminDetailDocumentResponse getDetailDocument(@PathVariable Long documentId);
47+
48+
@Operation(summary = "어드민 자료실 수정 API")
49+
@PatchMapping(value = "/{documentId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
50+
@ResponseStatus(HttpStatus.NO_CONTENT)
51+
@SecurityRequirement(name = "AccessToken")
52+
void modifyDocument(@PathVariable Long documentId,
53+
@ModelAttribute ModifyDocumentRequest modifyDocumentRequest,
54+
@RequestPart(name = "uploadFiles", required = false) List<MultipartFile> uploadFiles);
55+
56+
@Operation(summary = "어드민 자료실 삭제 API")
57+
@DeleteMapping("/{documentId}")
58+
@ResponseStatus(HttpStatus.NO_CONTENT)
59+
@SecurityRequirement(name = "AccessToken")
60+
void deleteDocument(@PathVariable Long documentId);
61+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package ddingdong.ddingdongBE.domain.documents.api;
2+
3+
4+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.DetailDocumentResponse;
5+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.DocumentResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import java.util.List;
10+
import org.springframework.http.HttpStatus;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.PathVariable;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.ResponseStatus;
15+
16+
@Tag(name = "Document", description = "Document API")
17+
@RequestMapping("/server/documents")
18+
public interface DocumentApi {
19+
20+
@Operation(summary = "자료실 목록 조회 API")
21+
@GetMapping
22+
@ResponseStatus(HttpStatus.OK)
23+
@SecurityRequirement(name = "AccessToken")
24+
List<DocumentResponse> getAllDocuments();
25+
26+
@Operation(summary = "자료실 상세 조회 API")
27+
@GetMapping("/{documentId}")
28+
@ResponseStatus(HttpStatus.OK)
29+
@SecurityRequirement(name = "AccessToken")
30+
DetailDocumentResponse getDetailDocument(@PathVariable Long documentId);
31+
32+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package ddingdong.ddingdongBE.domain.documents.controller;
2+
3+
import static ddingdong.ddingdongBE.domain.fileinformation.entity.FileDomainCategory.DOCUMENT;
4+
import static ddingdong.ddingdongBE.domain.fileinformation.entity.FileTypeCategory.FILE;
5+
6+
import ddingdong.ddingdongBE.domain.documents.api.AdminDocumentApi;
7+
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.GenerateDocumentRequest;
8+
import ddingdong.ddingdongBE.domain.documents.controller.dto.request.ModifyDocumentRequest;
9+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.AdminDetailDocumentResponse;
10+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.AdminDocumentResponse;
11+
import ddingdong.ddingdongBE.domain.documents.entity.Document;
12+
import ddingdong.ddingdongBE.domain.documents.service.DocumentService;
13+
import ddingdong.ddingdongBE.domain.fileinformation.service.FileInformationService;
14+
import ddingdong.ddingdongBE.file.dto.FileResponse;
15+
import ddingdong.ddingdongBE.file.service.FileService;
16+
import java.util.List;
17+
import lombok.RequiredArgsConstructor;
18+
import org.springframework.web.bind.annotation.ModelAttribute;
19+
import org.springframework.web.bind.annotation.PathVariable;
20+
import org.springframework.web.bind.annotation.RequestPart;
21+
import org.springframework.web.bind.annotation.RestController;
22+
import org.springframework.web.multipart.MultipartFile;
23+
24+
@RestController
25+
@RequiredArgsConstructor
26+
public class AdminDocumentController implements AdminDocumentApi {
27+
28+
private final DocumentService documentService;
29+
private final FileService fileService;
30+
private final FileInformationService fileInformationService;
31+
32+
public void generateDocument(@ModelAttribute GenerateDocumentRequest generateDocumentRequest,
33+
@RequestPart(name = "uploadFiles") List<MultipartFile> uploadFiles) {
34+
Long createdDocumentId = documentService.create(generateDocumentRequest.toEntity());
35+
fileService.uploadDownloadableFile(createdDocumentId, uploadFiles, FILE, DOCUMENT);
36+
}
37+
38+
public List<AdminDocumentResponse> getAllDocuments() {
39+
return documentService.getAll().stream()
40+
.map(AdminDocumentResponse::from)
41+
.toList();
42+
}
43+
44+
public AdminDetailDocumentResponse getDetailDocument(@PathVariable Long documentId) {
45+
Document document = documentService.getById(documentId);
46+
List<FileResponse> fileResponse = fileInformationService.getFileUrls(
47+
FILE.getFileType() + DOCUMENT.getFileDomain() + document.getId());
48+
return AdminDetailDocumentResponse.of(document, fileResponse);
49+
}
50+
51+
public void modifyDocument(@PathVariable Long documentId,
52+
@ModelAttribute ModifyDocumentRequest modifyDocumentRequest,
53+
@RequestPart(name = "uploadFiles", required = false) List<MultipartFile> uploadFiles) {
54+
Long updateDocumentId = documentService.update(documentId, modifyDocumentRequest.toEntity());
55+
fileService.deleteFile(updateDocumentId, FILE, DOCUMENT);
56+
fileService.uploadDownloadableFile(updateDocumentId, uploadFiles, FILE, DOCUMENT);
57+
}
58+
59+
public void deleteDocument(@PathVariable Long documentId) {
60+
fileService.deleteFile(documentId, FILE, DOCUMENT);
61+
documentService.delete(documentId);
62+
}
63+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package ddingdong.ddingdongBE.domain.documents.controller;
2+
3+
import static ddingdong.ddingdongBE.domain.fileinformation.entity.FileDomainCategory.DOCUMENT;
4+
import static ddingdong.ddingdongBE.domain.fileinformation.entity.FileTypeCategory.FILE;
5+
6+
import ddingdong.ddingdongBE.domain.documents.api.DocumentApi;
7+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.DetailDocumentResponse;
8+
import ddingdong.ddingdongBE.domain.documents.controller.dto.response.DocumentResponse;
9+
import ddingdong.ddingdongBE.domain.documents.entity.Document;
10+
import ddingdong.ddingdongBE.domain.documents.service.DocumentService;
11+
import ddingdong.ddingdongBE.domain.fileinformation.service.FileInformationService;
12+
import ddingdong.ddingdongBE.file.dto.FileResponse;
13+
import java.util.List;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.web.bind.annotation.PathVariable;
16+
import org.springframework.web.bind.annotation.RestController;
17+
18+
@RestController
19+
@RequiredArgsConstructor
20+
public class DocumentController implements DocumentApi {
21+
22+
private final DocumentService documentService;
23+
private final FileInformationService fileInformationService;
24+
25+
public List<DocumentResponse> getAllDocuments() {
26+
return documentService.getAll().stream()
27+
.map(DocumentResponse::from)
28+
.toList();
29+
}
30+
31+
public DetailDocumentResponse getDetailDocument(@PathVariable Long documentId) {
32+
Document document = documentService.getById(documentId);
33+
List<FileResponse> fileResponse = fileInformationService.getFileUrls(
34+
FILE.getFileType() + DOCUMENT.getFileDomain() + document.getId());
35+
return DetailDocumentResponse.of(document, fileResponse);
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package ddingdong.ddingdongBE.domain.documents.controller.dto.request;
2+
3+
import ddingdong.ddingdongBE.domain.documents.entity.Document;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
9+
@Schema(
10+
name = "GenerateDocumentRequest",
11+
description = "자료실 자료 생성 요청"
12+
)
13+
@Builder
14+
public record GenerateDocumentRequest(
15+
@Schema(description = "자료 제목", example = "제목")
16+
String title,
17+
18+
@Schema(description = "자료 내용", example = "내용")
19+
String content
20+
) {
21+
public Document toEntity() {
22+
return Document.builder()
23+
.title(title)
24+
.content(content)
25+
.build();
26+
}
27+
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package ddingdong.ddingdongBE.domain.documents.controller.dto.request;
2+
3+
import ddingdong.ddingdongBE.domain.documents.entity.Document;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
9+
@Schema(
10+
name = "ModifyDocumentRequest",
11+
description = "자료실 자료 수정 요청"
12+
)
13+
@Builder
14+
public record ModifyDocumentRequest(
15+
@Schema(description = "자료 제목", example = "제목")
16+
String title,
17+
18+
@Schema(description = "자료 내용", example = "내용") String content
19+
) {
20+
21+
public Document toEntity() {
22+
return Document.builder()
23+
.title(title)
24+
.content(content)
25+
.build();
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package ddingdong.ddingdongBE.domain.documents.controller.dto.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonFormat;
4+
import ddingdong.ddingdongBE.domain.documents.entity.Document;
5+
import ddingdong.ddingdongBE.file.dto.FileResponse;
6+
import io.swagger.v3.oas.annotations.media.ArraySchema;
7+
import io.swagger.v3.oas.annotations.media.Schema;
8+
import java.time.LocalDate;
9+
import java.util.List;
10+
import lombok.Builder;
11+
12+
@Schema(
13+
name = "AdminDetailDocumentResponse",
14+
description = "어드민 자료실 자료 상세 조회 응답"
15+
)
16+
@Builder
17+
public record AdminDetailDocumentResponse(
18+
@Schema(description = "자료 제목", example = "자료 제목")
19+
String title,
20+
21+
@Schema(description = "자료 내용", example = "자료 내용")
22+
String content,
23+
24+
@Schema(description = "작성일", example = "2024-01-01")
25+
@JsonFormat(pattern = "yyyy-MM-dd")
26+
LocalDate createdAt,
27+
28+
@ArraySchema(schema = @Schema(description = "첨부파일 목록", implementation = FileResponse.class))
29+
List<FileResponse> fileUrls
30+
) {
31+
32+
public static AdminDetailDocumentResponse of(Document document,
33+
List<FileResponse> fileResponses) {
34+
return AdminDetailDocumentResponse.builder()
35+
.title(document.getTitle())
36+
.content(document.getContent())
37+
.createdAt(document.getCreatedAt().toLocalDate())
38+
.fileUrls(fileResponses)
39+
.build();
40+
}
41+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package ddingdong.ddingdongBE.domain.documents.controller.dto.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonFormat;
4+
import ddingdong.ddingdongBE.domain.documents.entity.Document;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import java.time.LocalDate;
7+
import lombok.Builder;
8+
9+
@Schema(
10+
name = "AdminDocumentResponse",
11+
description = "어드민 자료실 자료 목록 조회 응답"
12+
)
13+
@Builder
14+
public record AdminDocumentResponse(
15+
@Schema(description = "자료 식별자", example = "1")
16+
Long id,
17+
18+
@Schema(description = "자료 제목", example = "자료 제목")
19+
String title,
20+
21+
@Schema(description = "작성일", example = "2024-01-01")
22+
@JsonFormat(pattern = "yyyy-MM-dd")
23+
LocalDate createdAt
24+
) {
25+
26+
public static AdminDocumentResponse from(Document document) {
27+
return AdminDocumentResponse.builder()
28+
.id(document.getId())
29+
.title(document.getTitle())
30+
.createdAt(document.getCreatedAt().toLocalDate())
31+
.build();
32+
}
33+
}

0 commit comments

Comments
 (0)