From 3c4259b10d889c15cda8ad07e05ab3cd814747f2 Mon Sep 17 00:00:00 2001 From: Jiwoo Kim Date: Fri, 15 Oct 2021 22:20:39 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EB=AC=B8=EC=84=9C=20=EB=8B=A8?= =?UTF-8?q?=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../learning/ApprovalState.java | 4 +- .../{playground => }/learning/Category.java | 2 +- .../{playground => }/learning/Document.java | 2 +- .../learning/DocumentApproval.java | 2 +- .../learning/DocumentApprovals.java | 6 +-- .../java/{playground => }/learning/User.java | 2 +- .../document/DocumentController.java | 15 ++++++++ .../document/DocumentControllerImpl.java | 28 ++++++++++++++ .../playground/document/DocumentService.java | 8 ++++ .../document/DocumentServiceImpl.java | 33 +++++++++++++++++ .../playground/document/dao/DocumentDao.java | 8 ++++ .../document/dao/DocumentH2Dao.java | 21 +++++++++++ .../document/dao/DocumentRowMapper.java | 29 +++++++++++++++ .../playground/document/dto/DocumentDto.java | 37 +++++++++++++++++++ .../document/entity/ApprovalState.java | 27 ++++++++++++++ .../playground/document/entity/Category.java | 15 ++++++++ .../playground/document/entity/Document.java | 17 +++++++++ src/main/java/playground/test.http | 9 +++++ .../java/playground/user/dao/UserDao.java | 9 +++++ .../java/playground/user/dao/UserH2Dao.java | 23 ++++++++++++ .../playground/user/dao/UserRowMapper.java | 20 ++++++++++ .../java/playground/user/entity/User.java | 12 ++++++ src/main/resources/application.yaml | 6 +++ src/main/resources/data.sql | 33 +++++++++++++++++ .../playground/learning/DocumentTest.java | 1 + 26 files changed, 361 insertions(+), 10 deletions(-) rename src/main/java/{playground => }/learning/ApprovalState.java (95%) rename src/main/java/{playground => }/learning/Category.java (89%) rename src/main/java/{playground => }/learning/Document.java (97%) rename src/main/java/{playground => }/learning/DocumentApproval.java (97%) rename src/main/java/{playground => }/learning/DocumentApprovals.java (91%) rename src/main/java/{playground => }/learning/User.java (86%) create mode 100644 src/main/java/playground/document/DocumentController.java create mode 100644 src/main/java/playground/document/DocumentControllerImpl.java create mode 100644 src/main/java/playground/document/DocumentService.java create mode 100644 src/main/java/playground/document/DocumentServiceImpl.java create mode 100644 src/main/java/playground/document/dao/DocumentDao.java create mode 100644 src/main/java/playground/document/dao/DocumentH2Dao.java create mode 100644 src/main/java/playground/document/dao/DocumentRowMapper.java create mode 100644 src/main/java/playground/document/dto/DocumentDto.java create mode 100644 src/main/java/playground/document/entity/ApprovalState.java create mode 100644 src/main/java/playground/document/entity/Category.java create mode 100644 src/main/java/playground/document/entity/Document.java create mode 100644 src/main/java/playground/test.http create mode 100644 src/main/java/playground/user/dao/UserDao.java create mode 100644 src/main/java/playground/user/dao/UserH2Dao.java create mode 100644 src/main/java/playground/user/dao/UserRowMapper.java create mode 100644 src/main/java/playground/user/entity/User.java create mode 100644 src/main/resources/application.yaml create mode 100644 src/main/resources/data.sql diff --git a/build.gradle b/build.gradle index fe00c55..1ad792b 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,7 @@ dependencies { testImplementation('org.springframework.boot:spring-boot-starter-test') // JDBC -// implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' // JPA // implementation 'org.springframework.boot:spring-boot-starter-data-jpa' diff --git a/src/main/java/playground/learning/ApprovalState.java b/src/main/java/learning/ApprovalState.java similarity index 95% rename from src/main/java/playground/learning/ApprovalState.java rename to src/main/java/learning/ApprovalState.java index dfc8e7b..659d4cb 100644 --- a/src/main/java/playground/learning/ApprovalState.java +++ b/src/main/java/learning/ApprovalState.java @@ -1,4 +1,4 @@ -package playground.learning; +package learning; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -21,7 +21,7 @@ public ApprovalState approve() { if (this == CANCELED) { throw new IllegalArgumentException("이미 결재자로부터 거절된 문서입니다."); } - + return APPROVED; } diff --git a/src/main/java/playground/learning/Category.java b/src/main/java/learning/Category.java similarity index 89% rename from src/main/java/playground/learning/Category.java rename to src/main/java/learning/Category.java index 6570774..5365213 100644 --- a/src/main/java/playground/learning/Category.java +++ b/src/main/java/learning/Category.java @@ -1,4 +1,4 @@ -package playground.learning; +package learning; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/playground/learning/Document.java b/src/main/java/learning/Document.java similarity index 97% rename from src/main/java/playground/learning/Document.java rename to src/main/java/learning/Document.java index ce6fe5d..bb3d57b 100644 --- a/src/main/java/playground/learning/Document.java +++ b/src/main/java/learning/Document.java @@ -1,4 +1,4 @@ -package playground.learning; +package learning; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/playground/learning/DocumentApproval.java b/src/main/java/learning/DocumentApproval.java similarity index 97% rename from src/main/java/playground/learning/DocumentApproval.java rename to src/main/java/learning/DocumentApproval.java index 8be4f13..3789a22 100644 --- a/src/main/java/playground/learning/DocumentApproval.java +++ b/src/main/java/learning/DocumentApproval.java @@ -1,4 +1,4 @@ -package playground.learning; +package learning; import lombok.Builder; diff --git a/src/main/java/playground/learning/DocumentApprovals.java b/src/main/java/learning/DocumentApprovals.java similarity index 91% rename from src/main/java/playground/learning/DocumentApprovals.java rename to src/main/java/learning/DocumentApprovals.java index ae0aa82..a009d56 100644 --- a/src/main/java/playground/learning/DocumentApprovals.java +++ b/src/main/java/learning/DocumentApprovals.java @@ -1,4 +1,4 @@ -package playground.learning; +package learning; import java.util.ArrayList; import java.util.List; @@ -20,8 +20,8 @@ public static DocumentApprovals empty() { public void addApprovals(List approvers) { List documentApprovals = IntStream.range(0, approvers.size()) - .mapToObj(index -> DocumentApproval.of(approvers.get(index), index + 1)) - .collect(Collectors.toList()); + .mapToObj(index -> DocumentApproval.of(approvers.get(index), index + 1)) + .collect(Collectors.toList()); this.approvals.addAll(documentApprovals); } diff --git a/src/main/java/playground/learning/User.java b/src/main/java/learning/User.java similarity index 86% rename from src/main/java/playground/learning/User.java rename to src/main/java/learning/User.java index de8ec69..96cebc1 100644 --- a/src/main/java/playground/learning/User.java +++ b/src/main/java/learning/User.java @@ -1,4 +1,4 @@ -package playground.learning; +package learning; import lombok.Builder; import lombok.EqualsAndHashCode; diff --git a/src/main/java/playground/document/DocumentController.java b/src/main/java/playground/document/DocumentController.java new file mode 100644 index 0000000..169be2a --- /dev/null +++ b/src/main/java/playground/document/DocumentController.java @@ -0,0 +1,15 @@ +package playground.document; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import playground.document.dto.DocumentDto; + +@RequestMapping( + path = "/api/documents", + produces = MediaType.APPLICATION_JSON_VALUE +) +public interface DocumentController { + + ResponseEntity findDocument(Long id); +} diff --git a/src/main/java/playground/document/DocumentControllerImpl.java b/src/main/java/playground/document/DocumentControllerImpl.java new file mode 100644 index 0000000..4c4feed --- /dev/null +++ b/src/main/java/playground/document/DocumentControllerImpl.java @@ -0,0 +1,28 @@ +package playground.document; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import playground.document.dto.DocumentDto; + +@RestController +@RequiredArgsConstructor +public class DocumentControllerImpl implements DocumentController { + + private final DocumentService documentService; + + @Override + @GetMapping(value = "/{id}") + public ResponseEntity findDocument(@PathVariable Long id) { + DocumentDto result = documentService.findById(id); + + return ResponseEntity + .status(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(result); + } +} diff --git a/src/main/java/playground/document/DocumentService.java b/src/main/java/playground/document/DocumentService.java new file mode 100644 index 0000000..662d3d4 --- /dev/null +++ b/src/main/java/playground/document/DocumentService.java @@ -0,0 +1,8 @@ +package playground.document; + +import playground.document.dto.DocumentDto; + +public interface DocumentService { + + DocumentDto findById(Long id); +} diff --git a/src/main/java/playground/document/DocumentServiceImpl.java b/src/main/java/playground/document/DocumentServiceImpl.java new file mode 100644 index 0000000..0e26432 --- /dev/null +++ b/src/main/java/playground/document/DocumentServiceImpl.java @@ -0,0 +1,33 @@ +package playground.document; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.stereotype.Service; +import playground.document.dao.DocumentDao; +import playground.document.dto.DocumentDto; +import playground.document.entity.Document; +import playground.user.dao.UserDao; +import playground.user.entity.User; + +import java.util.NoSuchElementException; + +@Service +@RequiredArgsConstructor +public class DocumentServiceImpl implements DocumentService { + + private final DocumentDao documentDao; + private final UserDao userDao; + + @Override + public DocumentDto findById(Long id) { + try { + // dao를 분리해서 각각 찾아오기 + Document document = documentDao.findById(id); + User drafter = userDao.findDrafterOf(document); + return DocumentDto.of(document, drafter); + // 테이블 조인해서 한 번에 찾아오기 -> service 단에서 sql을 관리해야 해서 별로임 + } catch (IncorrectResultSizeDataAccessException e) { + throw new NoSuchElementException("id에 맞는 document가 없음"); + } + } +} diff --git a/src/main/java/playground/document/dao/DocumentDao.java b/src/main/java/playground/document/dao/DocumentDao.java new file mode 100644 index 0000000..74174a5 --- /dev/null +++ b/src/main/java/playground/document/dao/DocumentDao.java @@ -0,0 +1,8 @@ +package playground.document.dao; + +import playground.document.entity.Document; + +public interface DocumentDao { + + Document findById(Long id); +} diff --git a/src/main/java/playground/document/dao/DocumentH2Dao.java b/src/main/java/playground/document/dao/DocumentH2Dao.java new file mode 100644 index 0000000..daa1629 --- /dev/null +++ b/src/main/java/playground/document/dao/DocumentH2Dao.java @@ -0,0 +1,21 @@ +package playground.document.dao; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import playground.document.entity.Document; + +@Repository +@RequiredArgsConstructor +public class DocumentH2Dao implements DocumentDao { + + private final JdbcTemplate jdbcTemplate; + private final DocumentRowMapper documentRowMapper; + + @Override + public Document findById(Long id) throws IncorrectResultSizeDataAccessException { + String query = "SELECT * FROM DOCUMENT WHERE ID=" + id; + return jdbcTemplate.queryForObject(query, documentRowMapper); + } +} diff --git a/src/main/java/playground/document/dao/DocumentRowMapper.java b/src/main/java/playground/document/dao/DocumentRowMapper.java new file mode 100644 index 0000000..45391cd --- /dev/null +++ b/src/main/java/playground/document/dao/DocumentRowMapper.java @@ -0,0 +1,29 @@ +package playground.document.dao; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import playground.document.entity.ApprovalState; +import playground.document.entity.Category; +import playground.document.entity.Document; +import playground.user.entity.User; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class DocumentRowMapper implements RowMapper { + + @Override + public Document mapRow(ResultSet rs, int rowNum) throws SQLException { + return Document.builder() + .id(rs.getLong("id")) + .title(rs.getString("title")) + .contents(rs.getString("contents")) + .drafter(User.builder() + .id(rs.getLong("drafter_id")) + .build()) + .category(Category.valueOf(rs.getString("category"))) + .approvalState(ApprovalState.valueOf(rs.getString("approval_state"))) + .build(); + } +} diff --git a/src/main/java/playground/document/dto/DocumentDto.java b/src/main/java/playground/document/dto/DocumentDto.java new file mode 100644 index 0000000..3e4bf31 --- /dev/null +++ b/src/main/java/playground/document/dto/DocumentDto.java @@ -0,0 +1,37 @@ +package playground.document.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import playground.document.entity.Document; +import playground.user.entity.User; + +@Getter +@Builder +@RequiredArgsConstructor +public class DocumentDto { + + private final Long id; + private final String title; + private final String contents; + private final Long userId; + private final String userName; + private final String category; + private final String categoryText; + private final String approvalState; + private final String approvalStateText; + + public static DocumentDto of(Document document, User drafter) { + return DocumentDto.builder() + .id(document.getId()) + .title(document.getTitle()) + .contents(document.getContents()) + .userId(drafter.getId()) + .userName(drafter.getName()) + .category(document.getCategory().name()) + .categoryText(document.getCategory().getText()) + .approvalState(document.getApprovalState().name()) + .approvalStateText(document.getApprovalState().getText()) + .build(); + } +} diff --git a/src/main/java/playground/document/entity/ApprovalState.java b/src/main/java/playground/document/entity/ApprovalState.java new file mode 100644 index 0000000..251bf1c --- /dev/null +++ b/src/main/java/playground/document/entity/ApprovalState.java @@ -0,0 +1,27 @@ +package playground.document.entity; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ApprovalState { + + DRAFTING("결재중"), + APPROVED("승인"), + CANCELED("거절"); + + private final String text; + + public boolean isDrafting() { + return this == DRAFTING; + } + + public boolean isApproved() { + return this == APPROVED; + } + + public boolean isCanceled() { + return this == CANCELED; + } +} diff --git a/src/main/java/playground/document/entity/Category.java b/src/main/java/playground/document/entity/Category.java new file mode 100644 index 0000000..7dceb70 --- /dev/null +++ b/src/main/java/playground/document/entity/Category.java @@ -0,0 +1,15 @@ +package playground.document.entity; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum Category { + + OPERATING_EXPENSES("운영비"), + EDUCATION("교육"), + PRODUCT_PURCHASING("물품구매"); + + private final String text; +} diff --git a/src/main/java/playground/document/entity/Document.java b/src/main/java/playground/document/entity/Document.java new file mode 100644 index 0000000..a870227 --- /dev/null +++ b/src/main/java/playground/document/entity/Document.java @@ -0,0 +1,17 @@ +package playground.document.entity; + +import lombok.Builder; +import lombok.Getter; +import playground.user.entity.User; + +@Builder +@Getter +public class Document { + + private Long id; + private String title; + private String contents; + private User drafter; + private Category category; + private ApprovalState approvalState; +} diff --git a/src/main/java/playground/test.http b/src/main/java/playground/test.http new file mode 100644 index 0000000..9f022e4 --- /dev/null +++ b/src/main/java/playground/test.http @@ -0,0 +1,9 @@ +GET http://localhost:8080/api/documents/1 + +HTTP/1.1 200 +Accept: application/json +Transfer-Encoding: chunked +Date: Tue, 24 Aug 2021 13:55:45 GMT +Keep-Alive: timeout=60 +Connection: keep-alive + diff --git a/src/main/java/playground/user/dao/UserDao.java b/src/main/java/playground/user/dao/UserDao.java new file mode 100644 index 0000000..c699d27 --- /dev/null +++ b/src/main/java/playground/user/dao/UserDao.java @@ -0,0 +1,9 @@ +package playground.user.dao; + +import playground.document.entity.Document; +import playground.user.entity.User; + +public interface UserDao { + + User findDrafterOf(Document document); +} diff --git a/src/main/java/playground/user/dao/UserH2Dao.java b/src/main/java/playground/user/dao/UserH2Dao.java new file mode 100644 index 0000000..0b3bc58 --- /dev/null +++ b/src/main/java/playground/user/dao/UserH2Dao.java @@ -0,0 +1,23 @@ +package playground.user.dao; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import playground.document.entity.Document; +import playground.user.entity.User; + +@Repository +@RequiredArgsConstructor +public class UserH2Dao implements UserDao { + + private final JdbcTemplate jdbcTemplate; + private final UserRowMapper userRowMapper; + + @Override + public User findDrafterOf(Document document) throws IncorrectResultSizeDataAccessException { + Long id = document.getDrafter().getId(); + String query = "SELECT * FROM USER WHERE ID=" + id; + return jdbcTemplate.queryForObject(query, userRowMapper); + } +} diff --git a/src/main/java/playground/user/dao/UserRowMapper.java b/src/main/java/playground/user/dao/UserRowMapper.java new file mode 100644 index 0000000..8feb3dc --- /dev/null +++ b/src/main/java/playground/user/dao/UserRowMapper.java @@ -0,0 +1,20 @@ +package playground.user.dao; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import playground.user.entity.User; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class UserRowMapper implements RowMapper { + + @Override + public User mapRow(ResultSet rs, int rowNum) throws SQLException { + return User.builder() + .id(rs.getLong("id")) + .name(rs.getString("name")) + .build(); + } +} diff --git a/src/main/java/playground/user/entity/User.java b/src/main/java/playground/user/entity/User.java new file mode 100644 index 0000000..859a1bb --- /dev/null +++ b/src/main/java/playground/user/entity/User.java @@ -0,0 +1,12 @@ +package playground.user.entity; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class User { + + private Long id; + private String name; +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..f921f4c --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,6 @@ +spring: + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:testdb + username: sa + password: diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..a150b64 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,33 @@ +drop table if exists user; +drop table if exists document; + +create table user +( + id bigint not null auto_increment primary key, + name varchar(255) not null +); + + +create table document +( + id bigint not null auto_increment primary key, + title varchar(255) not null, + contents varchar(255), + drafter_id bigint not null, + category varchar(255) not null, + approval_state varchar(255) not null, + foreign key (drafter_id) references user (id) + +); + +insert into user(name) +values ('유저1'); + +insert into user(name) +values ('유저2'); + +insert into document(title, contents, drafter_id, category, approval_state) +values ('1번 문서', '내용1', 1, 'OPERATING_EXPENSES', 'DRAFTING'); + +insert into document(title, contents, drafter_id, category, approval_state) +values ('2번 문서', '내용2', 1, 'OPERATING_EXPENSES', 'APPROVED'); \ No newline at end of file diff --git a/src/test/java/playground/learning/DocumentTest.java b/src/test/java/playground/learning/DocumentTest.java index 2ebfafb..4870f45 100644 --- a/src/test/java/playground/learning/DocumentTest.java +++ b/src/test/java/playground/learning/DocumentTest.java @@ -1,5 +1,6 @@ package playground.learning; +import learning.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From 313d2e3af7e8e9be313f4d2e34e0d4774c16e3b2 Mon Sep 17 00:00:00 2001 From: Jiwoo Kim Date: Mon, 25 Oct 2021 02:26:03 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EB=AC=B8=EC=84=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=9A=94=EC=B2=AD=20=EA=B5=AC=ED=98=84=20=EB=B0=8F?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 43 +++++++++++-- .../document/DocumentController.java | 15 ----- .../playground/document/DocumentService.java | 8 --- .../document/DocumentServiceImpl.java | 33 ---------- .../playground/document/dao/DocumentDao.java | 8 --- .../document/dao/DocumentH2Dao.java | 21 ------ .../document/entity/ApprovalState.java | 27 -------- .../playground/document/entity/Document.java | 17 ----- .../domain/document/DocumentController.java | 16 +++++ .../document/DocumentControllerImpl.java | 22 +++++-- .../domain/document/DocumentService.java | 11 ++++ .../domain/document/DocumentServiceImpl.java | 56 ++++++++++++++++ .../document/dao/DocumentApprovalDao.java | 8 +++ .../domain/document/dao/DocumentDao.java | 11 ++++ .../document/dao/DocumentRowMapper.java | 10 +-- .../dao/h2/DocumentApprovalH2Dao.java | 33 ++++++++++ .../domain/document/dao/h2/DocumentH2Dao.java | 38 +++++++++++ .../dto/AddDocumentApprovalParam.java | 23 +++++++ .../domain/document/dto/AddDocumentParam.java | 27 ++++++++ .../document/dto/AddDocumentRequest.java | 17 +++++ .../document/dto/DocumentDto.java | 6 +- .../dto/sql/AddDocumentApprovalSqlParam.java | 16 +++++ .../document/dto/sql/AddDocumentSqlParam.java | 17 +++++ .../domain/document/entity/ApprovalState.java | 17 +++++ .../document/entity/Category.java | 2 +- .../domain/document/entity/Document.java | 33 ++++++++++ .../playground/domain/user/dao/UserDao.java | 9 +++ .../{ => domain}/user/dao/UserH2Dao.java | 9 ++- .../{ => domain}/user/dao/UserRowMapper.java | 4 +- .../playground/domain/user/entity/User.java | 27 ++++++++ src/main/java/playground/test.http | 16 ++++- .../java/playground/user/dao/UserDao.java | 9 --- .../java/playground/user/entity/User.java | 12 ---- src/main/resources/application.yaml | 4 +- src/main/resources/data.sql | 22 ------- src/main/resources/schema.sql | 29 +++++++++ .../learning/DocumentTest.java | 3 +- .../dao/h2/DocumentApprovalH2DaoTest.java | 50 +++++++++++++++ .../document/dao/h2/DocumentH2DaoTest.java | 64 +++++++++++++++++++ .../domain/document/entity/DocumentTest.java | 30 +++++++++ .../domain/user/dao/UserH2DaoTest.java | 36 +++++++++++ .../domain/user/entity/UserTest.java | 30 +++++++++ 42 files changed, 683 insertions(+), 206 deletions(-) delete mode 100644 src/main/java/playground/document/DocumentController.java delete mode 100644 src/main/java/playground/document/DocumentService.java delete mode 100644 src/main/java/playground/document/DocumentServiceImpl.java delete mode 100644 src/main/java/playground/document/dao/DocumentDao.java delete mode 100644 src/main/java/playground/document/dao/DocumentH2Dao.java delete mode 100644 src/main/java/playground/document/entity/ApprovalState.java delete mode 100644 src/main/java/playground/document/entity/Document.java create mode 100644 src/main/java/playground/domain/document/DocumentController.java rename src/main/java/playground/{ => domain}/document/DocumentControllerImpl.java (50%) create mode 100644 src/main/java/playground/domain/document/DocumentService.java create mode 100644 src/main/java/playground/domain/document/DocumentServiceImpl.java create mode 100644 src/main/java/playground/domain/document/dao/DocumentApprovalDao.java create mode 100644 src/main/java/playground/domain/document/dao/DocumentDao.java rename src/main/java/playground/{ => domain}/document/dao/DocumentRowMapper.java (77%) create mode 100644 src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java create mode 100644 src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java create mode 100644 src/main/java/playground/domain/document/dto/AddDocumentApprovalParam.java create mode 100644 src/main/java/playground/domain/document/dto/AddDocumentParam.java create mode 100644 src/main/java/playground/domain/document/dto/AddDocumentRequest.java rename src/main/java/playground/{ => domain}/document/dto/DocumentDto.java (89%) create mode 100644 src/main/java/playground/domain/document/dto/sql/AddDocumentApprovalSqlParam.java create mode 100644 src/main/java/playground/domain/document/dto/sql/AddDocumentSqlParam.java create mode 100644 src/main/java/playground/domain/document/entity/ApprovalState.java rename src/main/java/playground/{ => domain}/document/entity/Category.java (85%) create mode 100644 src/main/java/playground/domain/document/entity/Document.java create mode 100644 src/main/java/playground/domain/user/dao/UserDao.java rename src/main/java/playground/{ => domain}/user/dao/UserH2Dao.java (64%) rename src/main/java/playground/{ => domain}/user/dao/UserRowMapper.java (85%) create mode 100644 src/main/java/playground/domain/user/entity/User.java delete mode 100644 src/main/java/playground/user/dao/UserDao.java delete mode 100644 src/main/java/playground/user/entity/User.java create mode 100644 src/main/resources/schema.sql rename src/test/java/{playground => }/learning/DocumentTest.java (99%) create mode 100644 src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java create mode 100644 src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java create mode 100644 src/test/java/playground/domain/document/entity/DocumentTest.java create mode 100644 src/test/java/playground/domain/user/dao/UserH2DaoTest.java create mode 100644 src/test/java/playground/domain/user/entity/UserTest.java diff --git a/README.md b/README.md index faaef33..51b3120 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,43 @@ -## TO-DO +## 배운점 -### Document +### SimpleJdbcInsertOperations, SqlParameterSource -- [ ] 결재자는 생성자에서만 추가하고 불변으로 관리해야 하지 않나? 왜 따로 add하지? -- [ ] 결재자 중복 확인이 필요할 것 같다. -- [ ] ApprovalState 필드를 가져야 하는가? DocumentApprovals에다가 물어보면 되는 거 아닌가? 연산 시간때문에 별도의 필드를 갖는게 나은가? +SQL을 하드코딩하는 것보다는 한 단계 추상화된 JDBC 기술들을 사용하는 게 더 낫다. -### DocumentApproval +- 오탈자 등 휴먼 에러로 인한 버그를 줄일 수 있다. +- 재사용성과 유지보수성이 개선된다. + - SQL을 직접 작성하면 끽해봐야 매직 넘버나 스트링을 빼는 정도로만 재사용성을 높일 수 있다. + - 반면 얘네들은 엔티티를 자동 매핑해주기도 하고... 스트링의 나열로 보는 것보다 가독성도 낫다. +- 그래도 여전히 불편하다. -- [ ] approvalOrder 필드를 가져야 하는가? 어차피 DocumentApprovals의 리스트가 순서를 다 알고 있지 않나? +## 의문점 +### 왜 엔티티를 쓸 일이 없는가 +DocumentApproval 엔티티를 만들지 않았는데도 API가 동작한다....? 이래도 되는 건가... 무슨 짓을 저지른 건가... +### documentService.findById() + +1. 테이블별로 메서드 분리해서 각각 찾아오기 + - document, user, document_approval 테이블 각각 조회...? 에반데 + - 게다가 SELECT문에서 조인해서 document 가져오면 document rowMapper도 같이 수정해야됨 진짜 구리다 + - 우선 response 형식에 approvers가 없으니까 document_approval 테이블은 패스 + - 근데 documentDao가 user 테이블까지 접근하는건 짱별로인듯 + +2. 테이블 조인해서 한 번에 찾아오기 + - 이럴거면 테이블별로 dao를 왜 나누냐..? + +### DB 테스트 시 초기 데이터로의 의존성 + +현재 dao 테스트가 data.sql에서 제공하는 초기 데이터셋에 의존하고 있다. + +- findById(): 문서가 이미 저장되어 있어야 find가 가능하다. +- addDocument(): pk를 검증할 수 없다. auto_increment pk 값을 모르기 때문이다. + +해결책은 뭘까 + +1. 그냥 초기 적재 데이터에 의존한다. + - 적재 데이터가 바뀌면 그 때마다 테스트가 깨지지 않나.. + - 영향 크게 안 받게 해놔도 언젠간 깨지지 않나.. +2. find 하기 전에 데이터를 넣던지, add 하기 전에 pk를 미리 알아 온다. + - 이전에 수행하는 로직에 의존하지 않나.. \ No newline at end of file diff --git a/src/main/java/playground/document/DocumentController.java b/src/main/java/playground/document/DocumentController.java deleted file mode 100644 index 169be2a..0000000 --- a/src/main/java/playground/document/DocumentController.java +++ /dev/null @@ -1,15 +0,0 @@ -package playground.document; - -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import playground.document.dto.DocumentDto; - -@RequestMapping( - path = "/api/documents", - produces = MediaType.APPLICATION_JSON_VALUE -) -public interface DocumentController { - - ResponseEntity findDocument(Long id); -} diff --git a/src/main/java/playground/document/DocumentService.java b/src/main/java/playground/document/DocumentService.java deleted file mode 100644 index 662d3d4..0000000 --- a/src/main/java/playground/document/DocumentService.java +++ /dev/null @@ -1,8 +0,0 @@ -package playground.document; - -import playground.document.dto.DocumentDto; - -public interface DocumentService { - - DocumentDto findById(Long id); -} diff --git a/src/main/java/playground/document/DocumentServiceImpl.java b/src/main/java/playground/document/DocumentServiceImpl.java deleted file mode 100644 index 0e26432..0000000 --- a/src/main/java/playground/document/DocumentServiceImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package playground.document; - -import lombok.RequiredArgsConstructor; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.stereotype.Service; -import playground.document.dao.DocumentDao; -import playground.document.dto.DocumentDto; -import playground.document.entity.Document; -import playground.user.dao.UserDao; -import playground.user.entity.User; - -import java.util.NoSuchElementException; - -@Service -@RequiredArgsConstructor -public class DocumentServiceImpl implements DocumentService { - - private final DocumentDao documentDao; - private final UserDao userDao; - - @Override - public DocumentDto findById(Long id) { - try { - // dao를 분리해서 각각 찾아오기 - Document document = documentDao.findById(id); - User drafter = userDao.findDrafterOf(document); - return DocumentDto.of(document, drafter); - // 테이블 조인해서 한 번에 찾아오기 -> service 단에서 sql을 관리해야 해서 별로임 - } catch (IncorrectResultSizeDataAccessException e) { - throw new NoSuchElementException("id에 맞는 document가 없음"); - } - } -} diff --git a/src/main/java/playground/document/dao/DocumentDao.java b/src/main/java/playground/document/dao/DocumentDao.java deleted file mode 100644 index 74174a5..0000000 --- a/src/main/java/playground/document/dao/DocumentDao.java +++ /dev/null @@ -1,8 +0,0 @@ -package playground.document.dao; - -import playground.document.entity.Document; - -public interface DocumentDao { - - Document findById(Long id); -} diff --git a/src/main/java/playground/document/dao/DocumentH2Dao.java b/src/main/java/playground/document/dao/DocumentH2Dao.java deleted file mode 100644 index daa1629..0000000 --- a/src/main/java/playground/document/dao/DocumentH2Dao.java +++ /dev/null @@ -1,21 +0,0 @@ -package playground.document.dao; - -import lombok.RequiredArgsConstructor; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; -import playground.document.entity.Document; - -@Repository -@RequiredArgsConstructor -public class DocumentH2Dao implements DocumentDao { - - private final JdbcTemplate jdbcTemplate; - private final DocumentRowMapper documentRowMapper; - - @Override - public Document findById(Long id) throws IncorrectResultSizeDataAccessException { - String query = "SELECT * FROM DOCUMENT WHERE ID=" + id; - return jdbcTemplate.queryForObject(query, documentRowMapper); - } -} diff --git a/src/main/java/playground/document/entity/ApprovalState.java b/src/main/java/playground/document/entity/ApprovalState.java deleted file mode 100644 index 251bf1c..0000000 --- a/src/main/java/playground/document/entity/ApprovalState.java +++ /dev/null @@ -1,27 +0,0 @@ -package playground.document.entity; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum ApprovalState { - - DRAFTING("결재중"), - APPROVED("승인"), - CANCELED("거절"); - - private final String text; - - public boolean isDrafting() { - return this == DRAFTING; - } - - public boolean isApproved() { - return this == APPROVED; - } - - public boolean isCanceled() { - return this == CANCELED; - } -} diff --git a/src/main/java/playground/document/entity/Document.java b/src/main/java/playground/document/entity/Document.java deleted file mode 100644 index a870227..0000000 --- a/src/main/java/playground/document/entity/Document.java +++ /dev/null @@ -1,17 +0,0 @@ -package playground.document.entity; - -import lombok.Builder; -import lombok.Getter; -import playground.user.entity.User; - -@Builder -@Getter -public class Document { - - private Long id; - private String title; - private String contents; - private User drafter; - private Category category; - private ApprovalState approvalState; -} diff --git a/src/main/java/playground/domain/document/DocumentController.java b/src/main/java/playground/domain/document/DocumentController.java new file mode 100644 index 0000000..43e258f --- /dev/null +++ b/src/main/java/playground/domain/document/DocumentController.java @@ -0,0 +1,16 @@ +package playground.domain.document; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import playground.domain.document.dto.AddDocumentRequest; +import playground.domain.document.dto.DocumentDto; + +@RequestMapping(path = "/api/documents", + produces = MediaType.APPLICATION_JSON_VALUE) +public interface DocumentController { + + ResponseEntity findDocument(Long id); + + ResponseEntity addDocument(AddDocumentRequest addDocumentRequest); +} diff --git a/src/main/java/playground/document/DocumentControllerImpl.java b/src/main/java/playground/domain/document/DocumentControllerImpl.java similarity index 50% rename from src/main/java/playground/document/DocumentControllerImpl.java rename to src/main/java/playground/domain/document/DocumentControllerImpl.java index 4c4feed..5e7365a 100644 --- a/src/main/java/playground/document/DocumentControllerImpl.java +++ b/src/main/java/playground/domain/document/DocumentControllerImpl.java @@ -1,13 +1,12 @@ -package playground.document; +package playground.domain.document; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; -import playground.document.dto.DocumentDto; +import org.springframework.web.bind.annotation.*; +import playground.domain.document.dto.AddDocumentRequest; +import playground.domain.document.dto.DocumentDto; @RestController @RequiredArgsConstructor @@ -16,7 +15,7 @@ public class DocumentControllerImpl implements DocumentController { private final DocumentService documentService; @Override - @GetMapping(value = "/{id}") + @GetMapping("/{id}") public ResponseEntity findDocument(@PathVariable Long id) { DocumentDto result = documentService.findById(id); @@ -25,4 +24,15 @@ public ResponseEntity findDocument(@PathVariable Long id) { .contentType(MediaType.APPLICATION_JSON) .body(result); } + + @Override + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addDocument(@RequestBody AddDocumentRequest addDocumentRequest) { + Long documentId = documentService.addDocument(addDocumentRequest); + + return ResponseEntity + .status(HttpStatus.ACCEPTED) + .contentType(MediaType.APPLICATION_JSON) + .body(documentId); + } } diff --git a/src/main/java/playground/domain/document/DocumentService.java b/src/main/java/playground/domain/document/DocumentService.java new file mode 100644 index 0000000..8b34139 --- /dev/null +++ b/src/main/java/playground/domain/document/DocumentService.java @@ -0,0 +1,11 @@ +package playground.domain.document; + +import playground.domain.document.dto.AddDocumentRequest; +import playground.domain.document.dto.DocumentDto; + +public interface DocumentService { + + DocumentDto findById(Long id); + + Long addDocument(AddDocumentRequest addDocumentRequest); +} diff --git a/src/main/java/playground/domain/document/DocumentServiceImpl.java b/src/main/java/playground/domain/document/DocumentServiceImpl.java new file mode 100644 index 0000000..5ff1e08 --- /dev/null +++ b/src/main/java/playground/domain/document/DocumentServiceImpl.java @@ -0,0 +1,56 @@ +package playground.domain.document; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.stereotype.Service; +import playground.domain.document.dao.DocumentApprovalDao; +import playground.domain.document.dao.DocumentDao; +import playground.domain.document.dto.AddDocumentApprovalParam; +import playground.domain.document.dto.AddDocumentParam; +import playground.domain.document.dto.AddDocumentRequest; +import playground.domain.document.dto.DocumentDto; +import playground.domain.document.entity.Document; +import playground.domain.user.dao.UserDao; +import playground.domain.user.entity.User; + +import java.util.NoSuchElementException; + +@Service +@RequiredArgsConstructor +public class DocumentServiceImpl implements DocumentService { + + private final DocumentDao documentDao; + private final DocumentApprovalDao documentApprovalDao; + private final UserDao userDao; + + @Override + public DocumentDto findById(Long id) { + try { + Document document = documentDao.findById(id); + User drafter = userDao.findDrafterOf(document); + return DocumentDto.of(document, drafter); + } catch (IncorrectResultSizeDataAccessException e) { + throw new NoSuchElementException("id에 맞는 document가 없음"); + } + } + + @Override + public Long addDocument(AddDocumentRequest addDocumentRequest) { + AddDocumentParam addDocumentParam = AddDocumentParam.of(addDocumentRequest); + Long documentId = documentDao.addDocument(addDocumentParam); + + AddDocumentApprovalParam addDocumentApprovalParam = AddDocumentApprovalParam.of(documentId, addDocumentRequest); + addDocumentApproval(addDocumentApprovalParam); + + return documentId; + } + + private void addDocumentApproval(AddDocumentApprovalParam addDocumentApprovalParam) { + try { + documentApprovalDao.addApprovals(addDocumentApprovalParam); + } catch (DataIntegrityViolationException e) { + throw new IllegalArgumentException("존재하지 않는 유저를 결재자로 등록함"); + } + } +} diff --git a/src/main/java/playground/domain/document/dao/DocumentApprovalDao.java b/src/main/java/playground/domain/document/dao/DocumentApprovalDao.java new file mode 100644 index 0000000..0eb849e --- /dev/null +++ b/src/main/java/playground/domain/document/dao/DocumentApprovalDao.java @@ -0,0 +1,8 @@ +package playground.domain.document.dao; + +import playground.domain.document.dto.AddDocumentApprovalParam; + +public interface DocumentApprovalDao { + + void addApprovals(AddDocumentApprovalParam addDocumentApprovalParam); +} diff --git a/src/main/java/playground/domain/document/dao/DocumentDao.java b/src/main/java/playground/domain/document/dao/DocumentDao.java new file mode 100644 index 0000000..ddce873 --- /dev/null +++ b/src/main/java/playground/domain/document/dao/DocumentDao.java @@ -0,0 +1,11 @@ +package playground.domain.document.dao; + +import playground.domain.document.dto.AddDocumentParam; +import playground.domain.document.entity.Document; + +public interface DocumentDao { + + Document findById(Long id); + + Long addDocument(AddDocumentParam addDocumentParam); +} diff --git a/src/main/java/playground/document/dao/DocumentRowMapper.java b/src/main/java/playground/domain/document/dao/DocumentRowMapper.java similarity index 77% rename from src/main/java/playground/document/dao/DocumentRowMapper.java rename to src/main/java/playground/domain/document/dao/DocumentRowMapper.java index 45391cd..1bdedf8 100644 --- a/src/main/java/playground/document/dao/DocumentRowMapper.java +++ b/src/main/java/playground/domain/document/dao/DocumentRowMapper.java @@ -1,11 +1,11 @@ -package playground.document.dao; +package playground.domain.document.dao; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; -import playground.document.entity.ApprovalState; -import playground.document.entity.Category; -import playground.document.entity.Document; -import playground.user.entity.User; +import playground.domain.document.entity.ApprovalState; +import playground.domain.document.entity.Category; +import playground.domain.document.entity.Document; +import playground.domain.user.entity.User; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java b/src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java new file mode 100644 index 0000000..8ba7fd0 --- /dev/null +++ b/src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java @@ -0,0 +1,33 @@ +package playground.domain.document.dao.h2; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import org.springframework.jdbc.core.simple.SimpleJdbcInsertOperations; +import org.springframework.stereotype.Repository; +import playground.domain.document.dao.DocumentApprovalDao; +import playground.domain.document.dto.AddDocumentApprovalParam; +import playground.domain.document.dto.sql.AddDocumentApprovalSqlParam; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class DocumentApprovalH2Dao implements DocumentApprovalDao { + + private final JdbcTemplate jdbcTemplate; + + @Override + public void addApprovals(AddDocumentApprovalParam addDocumentApprovalParam) throws DataIntegrityViolationException { + Long documentId = addDocumentApprovalParam.getDocumentId(); + List approversId = addDocumentApprovalParam.getApproversId(); + + SimpleJdbcInsertOperations insertOperations = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("DOCUMENT_APPROVAL"); + + approversId.stream() + .map(approverId -> AddDocumentApprovalSqlParam.of(documentId, approverId)) + .forEach(insertOperations::execute); + } +} diff --git a/src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java b/src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java new file mode 100644 index 0000000..5112617 --- /dev/null +++ b/src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java @@ -0,0 +1,38 @@ +package playground.domain.document.dao.h2; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import org.springframework.jdbc.core.simple.SimpleJdbcInsertOperations; +import org.springframework.stereotype.Repository; +import playground.domain.document.dao.DocumentDao; +import playground.domain.document.dao.DocumentRowMapper; +import playground.domain.document.dto.AddDocumentParam; +import playground.domain.document.dto.sql.AddDocumentSqlParam; +import playground.domain.document.entity.Document; + +@Repository +@RequiredArgsConstructor +public class DocumentH2Dao implements DocumentDao { + + private final JdbcTemplate jdbcTemplate; + private final DocumentRowMapper documentRowMapper; + + @Override + public Document findById(Long id) throws EmptyResultDataAccessException { + String query = "SELECT * FROM DOCUMENT WHERE ID=" + id; + return jdbcTemplate.queryForObject(query, documentRowMapper); + } + + @Override + public Long addDocument(AddDocumentParam addDocumentParam) { + SimpleJdbcInsertOperations insertOperations = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("DOCUMENT") + .usingGeneratedKeyColumns("ID"); + + return insertOperations + .executeAndReturnKey(AddDocumentSqlParam.of(addDocumentParam)) + .longValue(); + } +} diff --git a/src/main/java/playground/domain/document/dto/AddDocumentApprovalParam.java b/src/main/java/playground/domain/document/dto/AddDocumentApprovalParam.java new file mode 100644 index 0000000..b391336 --- /dev/null +++ b/src/main/java/playground/domain/document/dto/AddDocumentApprovalParam.java @@ -0,0 +1,23 @@ +package playground.domain.document.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@Builder +public class AddDocumentApprovalParam { + + private final Long documentId; + private final List approversId; + + public static AddDocumentApprovalParam of(Long documentId, AddDocumentRequest addDocumentRequest) { + return AddDocumentApprovalParam.builder() + .documentId(documentId) + .approversId(Collections.unmodifiableList(new ArrayList<>(addDocumentRequest.getApproverIds()))) + .build(); + } +} diff --git a/src/main/java/playground/domain/document/dto/AddDocumentParam.java b/src/main/java/playground/domain/document/dto/AddDocumentParam.java new file mode 100644 index 0000000..d9952fe --- /dev/null +++ b/src/main/java/playground/domain/document/dto/AddDocumentParam.java @@ -0,0 +1,27 @@ +package playground.domain.document.dto; + +import lombok.Builder; +import lombok.Getter; + +import static playground.domain.document.entity.ApprovalState.DEFAULT_APPROVAL_STATE_TEXT; + +@Getter +@Builder +public class AddDocumentParam { + + private final String title; + private final String contents; + private final Long drafterId; + private final String categoryText; + private final String approvalStateText; + + public static AddDocumentParam of(AddDocumentRequest addDocumentRequest) { + return AddDocumentParam.builder() + .title(addDocumentRequest.getTitle()) + .contents(addDocumentRequest.getContents()) + .drafterId(addDocumentRequest.getDrafterId()) + .categoryText(addDocumentRequest.getCategory()) + .approvalStateText(DEFAULT_APPROVAL_STATE_TEXT) + .build(); + } +} diff --git a/src/main/java/playground/domain/document/dto/AddDocumentRequest.java b/src/main/java/playground/domain/document/dto/AddDocumentRequest.java new file mode 100644 index 0000000..01f813d --- /dev/null +++ b/src/main/java/playground/domain/document/dto/AddDocumentRequest.java @@ -0,0 +1,17 @@ +package playground.domain.document.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Getter +@RequiredArgsConstructor +public class AddDocumentRequest { + + private final String title; + private final String category; + private final String contents; + private final Long drafterId; + private final List approverIds; +} diff --git a/src/main/java/playground/document/dto/DocumentDto.java b/src/main/java/playground/domain/document/dto/DocumentDto.java similarity index 89% rename from src/main/java/playground/document/dto/DocumentDto.java rename to src/main/java/playground/domain/document/dto/DocumentDto.java index 3e4bf31..38adc59 100644 --- a/src/main/java/playground/document/dto/DocumentDto.java +++ b/src/main/java/playground/domain/document/dto/DocumentDto.java @@ -1,10 +1,10 @@ -package playground.document.dto; +package playground.domain.document.dto; import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; -import playground.document.entity.Document; -import playground.user.entity.User; +import playground.domain.document.entity.Document; +import playground.domain.user.entity.User; @Getter @Builder diff --git a/src/main/java/playground/domain/document/dto/sql/AddDocumentApprovalSqlParam.java b/src/main/java/playground/domain/document/dto/sql/AddDocumentApprovalSqlParam.java new file mode 100644 index 0000000..9fb4796 --- /dev/null +++ b/src/main/java/playground/domain/document/dto/sql/AddDocumentApprovalSqlParam.java @@ -0,0 +1,16 @@ +package playground.domain.document.dto.sql; + +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; + +import static playground.domain.document.entity.ApprovalState.DEFAULT_APPROVAL_STATE_TEXT; + +public class AddDocumentApprovalSqlParam extends MapSqlParameterSource { + + public static SqlParameterSource of(Long documentId, Long approverId) { + return new MapSqlParameterSource() + .addValue("document_id", documentId) + .addValue("approver_id", approverId) + .addValue("approval_state", DEFAULT_APPROVAL_STATE_TEXT); + } +} diff --git a/src/main/java/playground/domain/document/dto/sql/AddDocumentSqlParam.java b/src/main/java/playground/domain/document/dto/sql/AddDocumentSqlParam.java new file mode 100644 index 0000000..484b8bc --- /dev/null +++ b/src/main/java/playground/domain/document/dto/sql/AddDocumentSqlParam.java @@ -0,0 +1,17 @@ +package playground.domain.document.dto.sql; + +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import playground.domain.document.dto.AddDocumentParam; + +public class AddDocumentSqlParam extends MapSqlParameterSource { + + public static SqlParameterSource of(AddDocumentParam addDocumentParam) { + return new MapSqlParameterSource() + .addValue("title", addDocumentParam.getTitle()) + .addValue("contents", addDocumentParam.getContents()) + .addValue("drafter_id", addDocumentParam.getDrafterId()) + .addValue("category", addDocumentParam.getCategoryText()) + .addValue("approval_state", addDocumentParam.getApprovalStateText()); + } +} diff --git a/src/main/java/playground/domain/document/entity/ApprovalState.java b/src/main/java/playground/domain/document/entity/ApprovalState.java new file mode 100644 index 0000000..3880558 --- /dev/null +++ b/src/main/java/playground/domain/document/entity/ApprovalState.java @@ -0,0 +1,17 @@ +package playground.domain.document.entity; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ApprovalState { + + DRAFTING("결재중"), + APPROVED("승인"), + CANCELED("거절"); + + private final String text; + + public static final String DEFAULT_APPROVAL_STATE_TEXT = DRAFTING.name(); +} diff --git a/src/main/java/playground/document/entity/Category.java b/src/main/java/playground/domain/document/entity/Category.java similarity index 85% rename from src/main/java/playground/document/entity/Category.java rename to src/main/java/playground/domain/document/entity/Category.java index 7dceb70..cde8158 100644 --- a/src/main/java/playground/document/entity/Category.java +++ b/src/main/java/playground/domain/document/entity/Category.java @@ -1,4 +1,4 @@ -package playground.document.entity; +package playground.domain.document.entity; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/playground/domain/document/entity/Document.java b/src/main/java/playground/domain/document/entity/Document.java new file mode 100644 index 0000000..4566382 --- /dev/null +++ b/src/main/java/playground/domain/document/entity/Document.java @@ -0,0 +1,33 @@ +package playground.domain.document.entity; + +import lombok.Builder; +import lombok.Getter; +import playground.domain.user.entity.User; + +import java.util.Objects; + +@Builder +@Getter +public class Document { + + private Long id; + private String title; + private String contents; + private User drafter; + private Category category; + private ApprovalState approvalState; +// private DocumentApprovals approvals; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Document document = (Document) o; + return Objects.equals(id, document.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/main/java/playground/domain/user/dao/UserDao.java b/src/main/java/playground/domain/user/dao/UserDao.java new file mode 100644 index 0000000..1394e2a --- /dev/null +++ b/src/main/java/playground/domain/user/dao/UserDao.java @@ -0,0 +1,9 @@ +package playground.domain.user.dao; + +import playground.domain.document.entity.Document; +import playground.domain.user.entity.User; + +public interface UserDao { + + User findDrafterOf(Document document); +} diff --git a/src/main/java/playground/user/dao/UserH2Dao.java b/src/main/java/playground/domain/user/dao/UserH2Dao.java similarity index 64% rename from src/main/java/playground/user/dao/UserH2Dao.java rename to src/main/java/playground/domain/user/dao/UserH2Dao.java index 0b3bc58..fa9fb2f 100644 --- a/src/main/java/playground/user/dao/UserH2Dao.java +++ b/src/main/java/playground/domain/user/dao/UserH2Dao.java @@ -1,11 +1,10 @@ -package playground.user.dao; +package playground.domain.user.dao; import lombok.RequiredArgsConstructor; -import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; -import playground.document.entity.Document; -import playground.user.entity.User; +import playground.domain.document.entity.Document; +import playground.domain.user.entity.User; @Repository @RequiredArgsConstructor @@ -15,7 +14,7 @@ public class UserH2Dao implements UserDao { private final UserRowMapper userRowMapper; @Override - public User findDrafterOf(Document document) throws IncorrectResultSizeDataAccessException { + public User findDrafterOf(Document document) { Long id = document.getDrafter().getId(); String query = "SELECT * FROM USER WHERE ID=" + id; return jdbcTemplate.queryForObject(query, userRowMapper); diff --git a/src/main/java/playground/user/dao/UserRowMapper.java b/src/main/java/playground/domain/user/dao/UserRowMapper.java similarity index 85% rename from src/main/java/playground/user/dao/UserRowMapper.java rename to src/main/java/playground/domain/user/dao/UserRowMapper.java index 8feb3dc..2d1abf2 100644 --- a/src/main/java/playground/user/dao/UserRowMapper.java +++ b/src/main/java/playground/domain/user/dao/UserRowMapper.java @@ -1,8 +1,8 @@ -package playground.user.dao; +package playground.domain.user.dao; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; -import playground.user.entity.User; +import playground.domain.user.entity.User; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/src/main/java/playground/domain/user/entity/User.java b/src/main/java/playground/domain/user/entity/User.java new file mode 100644 index 0000000..ef18bda --- /dev/null +++ b/src/main/java/playground/domain/user/entity/User.java @@ -0,0 +1,27 @@ +package playground.domain.user.entity; + +import lombok.Builder; +import lombok.Getter; + +import java.util.Objects; + +@Getter +@Builder +public class User { + + private Long id; + private String name; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/main/java/playground/test.http b/src/main/java/playground/test.http index 9f022e4..22f97c0 100644 --- a/src/main/java/playground/test.http +++ b/src/main/java/playground/test.http @@ -1,4 +1,4 @@ -GET http://localhost:8080/api/documents/1 +GET http://localhost:8080/api/documents/4 HTTP/1.1 200 Accept: application/json @@ -7,3 +7,17 @@ Date: Tue, 24 Aug 2021 13:55:45 GMT Keep-Alive: timeout=60 Connection: keep-alive +### +POST http://localhost:8080/api/documents +Content-Type: application/json + +{ + "title": "교육비 결재 요청", + "category": "EDUCATION", + "contents": "사외교육비 결재 요청드립니다.", + "drafterId": 1, + "approverIds": [ + 1, + 2 + ] +} \ No newline at end of file diff --git a/src/main/java/playground/user/dao/UserDao.java b/src/main/java/playground/user/dao/UserDao.java deleted file mode 100644 index c699d27..0000000 --- a/src/main/java/playground/user/dao/UserDao.java +++ /dev/null @@ -1,9 +0,0 @@ -package playground.user.dao; - -import playground.document.entity.Document; -import playground.user.entity.User; - -public interface UserDao { - - User findDrafterOf(Document document); -} diff --git a/src/main/java/playground/user/entity/User.java b/src/main/java/playground/user/entity/User.java deleted file mode 100644 index 859a1bb..0000000 --- a/src/main/java/playground/user/entity/User.java +++ /dev/null @@ -1,12 +0,0 @@ -package playground.user.entity; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class User { - - private Long id; - private String name; -} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index f921f4c..51fca4a 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,6 @@ spring: datasource: driver-class-name: org.h2.Driver - url: jdbc:h2:mem:testdb + url: jdbc:h2:mem:db username: sa - password: + password: \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index a150b64..58ea5f7 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,25 +1,3 @@ -drop table if exists user; -drop table if exists document; - -create table user -( - id bigint not null auto_increment primary key, - name varchar(255) not null -); - - -create table document -( - id bigint not null auto_increment primary key, - title varchar(255) not null, - contents varchar(255), - drafter_id bigint not null, - category varchar(255) not null, - approval_state varchar(255) not null, - foreign key (drafter_id) references user (id) - -); - insert into user(name) values ('유저1'); diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..a312d3d --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,29 @@ +drop table if exists user; +drop table if exists document; + +create table user +( + id bigint not null auto_increment primary key, + name varchar(255) not null +); + + +create table document +( + id bigint not null auto_increment primary key, + title varchar(255) not null, + contents varchar(255), + drafter_id bigint not null, + category varchar(255) not null, + approval_state varchar(255) not null, + foreign key (drafter_id) references user (id) +); + +create table document_approval +( + id bigint not null auto_increment primary key, + approver_id bigint not null, + approval_state varchar(255) not null, + approval_comment varchar(255), + foreign key (approver_id) references user (id) +); \ No newline at end of file diff --git a/src/test/java/playground/learning/DocumentTest.java b/src/test/java/learning/DocumentTest.java similarity index 99% rename from src/test/java/playground/learning/DocumentTest.java rename to src/test/java/learning/DocumentTest.java index 4870f45..09f373d 100644 --- a/src/test/java/playground/learning/DocumentTest.java +++ b/src/test/java/learning/DocumentTest.java @@ -1,6 +1,5 @@ -package playground.learning; +package learning; -import learning.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java b/src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java new file mode 100644 index 0000000..a307aea --- /dev/null +++ b/src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java @@ -0,0 +1,50 @@ +package playground.domain.document.dao.h2; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; +import playground.domain.document.dao.DocumentApprovalDao; +import playground.domain.document.dto.AddDocumentApprovalParam; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; + +@SpringBootTest +class DocumentApprovalH2DaoTest { + + @Autowired + private DocumentApprovalDao documentApprovalDao; + + @Test + @DisplayName("새 결재건을 추가한다.") + void add_document_approval_success() { + // given + AddDocumentApprovalParam addDocumentApprovalParam = AddDocumentApprovalParam.builder() + .documentId(1L) + .approversId(Arrays.asList(1L, 2L)) + .build(); + + // when, then + assertThatNoException() + .isThrownBy(() -> documentApprovalDao.addApprovals(addDocumentApprovalParam)); + } + + @Test + @DisplayName("존재하지 않는 유저를 결재자로 추가할 경우 예외가 발생한다.") + void add_document_approval_fail_no_such_approver() { + // given + AddDocumentApprovalParam addDocumentApprovalParam = AddDocumentApprovalParam.builder() + .documentId(1L) + .approversId(Collections.singletonList(999999L)) + .build(); + + // when, then + assertThatExceptionOfType(DataIntegrityViolationException.class) + .isThrownBy(() -> documentApprovalDao.addApprovals(addDocumentApprovalParam)); + } +} \ No newline at end of file diff --git a/src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java b/src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java new file mode 100644 index 0000000..81554bd --- /dev/null +++ b/src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java @@ -0,0 +1,64 @@ +package playground.domain.document.dao.h2; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.EmptyResultDataAccessException; +import playground.domain.document.dto.AddDocumentParam; +import playground.domain.document.entity.Document; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +@SpringBootTest +class DocumentH2DaoTest { + + @Autowired + private DocumentH2Dao documentH2Dao; + + @Test + @DisplayName("식별자로 문서를 찾아온다.") + void find_by_id_success() { + // given + Document expected = Document.builder() + .id(1L) + .build(); + + // when + Document result = documentH2Dao.findById(expected.getId()); + + // then + assertThat(result).isEqualTo(expected); + } + + @Test + @DisplayName("존재하지 않는 문서를 조회하면 예외가 발생한다.") + void find_by_id_fail_no_such_document() { + // given + Long id = 999999999L; + + // when, then + assertThatExceptionOfType(EmptyResultDataAccessException.class) + .isThrownBy(() -> documentH2Dao.findById(id)); + } + + @Test + @DisplayName("새 문서를 저장한다.") + void add_document_success() { + // given + AddDocumentParam addDocumentParam = AddDocumentParam.builder() + .title("title") + .contents("content") + .drafterId(1L) + .categoryText("category") + .approvalStateText("DRAFTING") + .build(); + + // when + Long documentId = documentH2Dao.addDocument(addDocumentParam); + + // then + assertThat(documentId).isNotNull(); + } +} \ No newline at end of file diff --git a/src/test/java/playground/domain/document/entity/DocumentTest.java b/src/test/java/playground/domain/document/entity/DocumentTest.java new file mode 100644 index 0000000..4ba325f --- /dev/null +++ b/src/test/java/playground/domain/document/entity/DocumentTest.java @@ -0,0 +1,30 @@ +package playground.domain.document.entity; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class DocumentTest { + + @Test + @DisplayName("식별자로 동등성을 비교한다.") + void equality_with_id() { + // given + Long id = 1L; + Document document_one = Document.builder() + .id(id) + .title("1번 문서") + .build(); + Document document_two = Document.builder() + .id(id) + .title("제목이 달라요") + .build(); + + // when + boolean result = document_one.equals(document_two); + + // then + assertThat(result).isTrue(); + } +} \ No newline at end of file diff --git a/src/test/java/playground/domain/user/dao/UserH2DaoTest.java b/src/test/java/playground/domain/user/dao/UserH2DaoTest.java new file mode 100644 index 0000000..0cfb8f1 --- /dev/null +++ b/src/test/java/playground/domain/user/dao/UserH2DaoTest.java @@ -0,0 +1,36 @@ +package playground.domain.user.dao; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import playground.domain.document.entity.Document; +import playground.domain.user.entity.User; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class UserH2DaoTest { + + @Autowired + private UserH2Dao userH2Dao; + + @Test + @DisplayName("문서가 주어지면 작성자 유저를 찾아온다.") + void find_drafter_of_success() { + // given + User expected = User.builder() + .id(1L) + .build(); + Document document = Document.builder() + .id(1L) + .drafter(expected) + .build(); + + // when + User result = userH2Dao.findDrafterOf(document); + + // then + assertThat(result).isEqualTo(expected); + } +} \ No newline at end of file diff --git a/src/test/java/playground/domain/user/entity/UserTest.java b/src/test/java/playground/domain/user/entity/UserTest.java new file mode 100644 index 0000000..120e100 --- /dev/null +++ b/src/test/java/playground/domain/user/entity/UserTest.java @@ -0,0 +1,30 @@ +package playground.domain.user.entity; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class UserTest { + + @Test + @DisplayName("식별자로 동등성을 비교한다.") + void equality_with_id() { + // given + Long id = 1L; + User user_one = User.builder() + .id(id) + .name("이름1") + .build(); + User user_two = User.builder() + .id(id) + .name("이름2") + .build(); + + // when + boolean result = user_one.equals(user_two); + + // then + assertThat(result).isTrue(); + } +} From 36fa57e5a38fc952bf3eac97ebcf22b4822d7bc3 Mon Sep 17 00:00:00 2001 From: Jiwoo Kim Date: Mon, 25 Oct 2021 03:39:38 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20OUTBOX=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++- .../domain/document/DocumentController.java | 9 ++++-- .../document/DocumentControllerImpl.java | 22 ++++++++++--- .../domain/document/DocumentService.java | 7 ++-- .../domain/document/DocumentServiceImpl.java | 22 ++++++++++--- .../document/dao/DocumentApprovalDao.java | 2 +- .../domain/document/dao/DocumentDao.java | 6 +++- .../document/dao/DocumentRowMapper.java | 1 + .../dao/h2/DocumentApprovalH2Dao.java | 4 +-- .../domain/document/dao/h2/DocumentH2Dao.java | 14 ++++++-- .../domain/document/dto/BoxDocument.java | 28 ++++++++++++++++ .../domain/document/dto/OutBox.java | 23 +++++++++++++ .../{DocumentDto.java => SingleDocument.java} | 6 ++-- .../{ => param}/AddDocumentApprovalParam.java | 3 +- .../dto/{ => param}/AddDocumentParam.java | 3 +- .../sql/AddDocumentApprovalSqlParam.java | 2 +- .../{ => param}/sql/AddDocumentSqlParam.java | 9 ++++-- .../domain/document/entity/Document.java | 2 ++ .../domain/document/entity/Documents.java | 29 +++++++++++++++++ src/main/java/playground/test.http | 14 +++----- src/main/resources/application.yaml | 5 ++- src/main/resources/data.sql | 8 ++--- src/main/resources/schema.sql | 1 + .../dao/h2/DocumentApprovalH2DaoTest.java | 2 +- .../document/dao/h2/DocumentH2DaoTest.java | 32 ++++++++++++++++++- 25 files changed, 217 insertions(+), 46 deletions(-) create mode 100644 src/main/java/playground/domain/document/dto/BoxDocument.java create mode 100644 src/main/java/playground/domain/document/dto/OutBox.java rename src/main/java/playground/domain/document/dto/{DocumentDto.java => SingleDocument.java} (88%) rename src/main/java/playground/domain/document/dto/{ => param}/AddDocumentApprovalParam.java (85%) rename src/main/java/playground/domain/document/dto/{ => param}/AddDocumentParam.java (88%) rename src/main/java/playground/domain/document/dto/{ => param}/sql/AddDocumentApprovalSqlParam.java (92%) rename src/main/java/playground/domain/document/dto/{ => param}/sql/AddDocumentSqlParam.java (74%) create mode 100644 src/main/java/playground/domain/document/entity/Documents.java diff --git a/README.md b/README.md index 51b3120..ee0a4e7 100644 --- a/README.md +++ b/README.md @@ -40,4 +40,11 @@ DocumentApproval 엔티티를 만들지 않았는데도 API가 동작한다....? - 적재 데이터가 바뀌면 그 때마다 테스트가 깨지지 않나.. - 영향 크게 안 받게 해놔도 언젠간 깨지지 않나.. 2. find 하기 전에 데이터를 넣던지, add 하기 전에 pk를 미리 알아 온다. - - 이전에 수행하는 로직에 의존하지 않나.. \ No newline at end of file + - 이전에 수행하는 로직에 의존하지 않나.. + +### 테스트 실행 방식에 따른 결과 차이 + +DocumentH2DaoTest의 find_by_drafter_success() 결과가 독립적이지 않다. + +- 하나만 따로 돌리면 성공하는데, 전체 테스트를 한 번에 돌리면 fail한다. +- 이유를 모르겠다.... 머냐... \ No newline at end of file diff --git a/src/main/java/playground/domain/document/DocumentController.java b/src/main/java/playground/domain/document/DocumentController.java index 43e258f..c4cfcf2 100644 --- a/src/main/java/playground/domain/document/DocumentController.java +++ b/src/main/java/playground/domain/document/DocumentController.java @@ -4,13 +4,18 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import playground.domain.document.dto.AddDocumentRequest; -import playground.domain.document.dto.DocumentDto; +import playground.domain.document.dto.BoxDocument; +import playground.domain.document.dto.SingleDocument; + +import java.util.List; @RequestMapping(path = "/api/documents", produces = MediaType.APPLICATION_JSON_VALUE) public interface DocumentController { - ResponseEntity findDocument(Long id); + ResponseEntity findDocument(Long id); ResponseEntity addDocument(AddDocumentRequest addDocumentRequest); + + ResponseEntity> findOutbox(Long drafterId); } diff --git a/src/main/java/playground/domain/document/DocumentControllerImpl.java b/src/main/java/playground/domain/document/DocumentControllerImpl.java index 5e7365a..796c073 100644 --- a/src/main/java/playground/domain/document/DocumentControllerImpl.java +++ b/src/main/java/playground/domain/document/DocumentControllerImpl.java @@ -6,7 +6,11 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import playground.domain.document.dto.AddDocumentRequest; -import playground.domain.document.dto.DocumentDto; +import playground.domain.document.dto.BoxDocument; +import playground.domain.document.dto.OutBox; +import playground.domain.document.dto.SingleDocument; + +import java.util.List; @RestController @RequiredArgsConstructor @@ -16,12 +20,11 @@ public class DocumentControllerImpl implements DocumentController { @Override @GetMapping("/{id}") - public ResponseEntity findDocument(@PathVariable Long id) { - DocumentDto result = documentService.findById(id); + public ResponseEntity findDocument(@PathVariable Long id) { + SingleDocument result = documentService.findById(id); return ResponseEntity .status(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON) .body(result); } @@ -32,7 +35,16 @@ public ResponseEntity addDocument(@RequestBody AddDocumentRequest addDocum return ResponseEntity .status(HttpStatus.ACCEPTED) - .contentType(MediaType.APPLICATION_JSON) .body(documentId); } + + @Override + @GetMapping("/outbox") + public ResponseEntity> findOutbox(@RequestParam Long drafterId) { + OutBox outbox = documentService.findOutboxOf(drafterId); + + return ResponseEntity + .status(HttpStatus.OK) + .body(outbox.getElements()); + } } diff --git a/src/main/java/playground/domain/document/DocumentService.java b/src/main/java/playground/domain/document/DocumentService.java index 8b34139..e503e74 100644 --- a/src/main/java/playground/domain/document/DocumentService.java +++ b/src/main/java/playground/domain/document/DocumentService.java @@ -1,11 +1,14 @@ package playground.domain.document; import playground.domain.document.dto.AddDocumentRequest; -import playground.domain.document.dto.DocumentDto; +import playground.domain.document.dto.OutBox; +import playground.domain.document.dto.SingleDocument; public interface DocumentService { - DocumentDto findById(Long id); + SingleDocument findById(Long id); Long addDocument(AddDocumentRequest addDocumentRequest); + + OutBox findOutboxOf(Long drafterId); } diff --git a/src/main/java/playground/domain/document/DocumentServiceImpl.java b/src/main/java/playground/domain/document/DocumentServiceImpl.java index 5ff1e08..07e6816 100644 --- a/src/main/java/playground/domain/document/DocumentServiceImpl.java +++ b/src/main/java/playground/domain/document/DocumentServiceImpl.java @@ -6,14 +6,17 @@ import org.springframework.stereotype.Service; import playground.domain.document.dao.DocumentApprovalDao; import playground.domain.document.dao.DocumentDao; -import playground.domain.document.dto.AddDocumentApprovalParam; -import playground.domain.document.dto.AddDocumentParam; import playground.domain.document.dto.AddDocumentRequest; -import playground.domain.document.dto.DocumentDto; +import playground.domain.document.dto.OutBox; +import playground.domain.document.dto.SingleDocument; +import playground.domain.document.dto.param.AddDocumentApprovalParam; +import playground.domain.document.dto.param.AddDocumentParam; import playground.domain.document.entity.Document; +import playground.domain.document.entity.Documents; import playground.domain.user.dao.UserDao; import playground.domain.user.entity.User; +import java.util.List; import java.util.NoSuchElementException; @Service @@ -25,11 +28,12 @@ public class DocumentServiceImpl implements DocumentService { private final UserDao userDao; @Override - public DocumentDto findById(Long id) { + public SingleDocument findById(Long id) { try { Document document = documentDao.findById(id); User drafter = userDao.findDrafterOf(document); - return DocumentDto.of(document, drafter); + + return SingleDocument.of(document, drafter); } catch (IncorrectResultSizeDataAccessException e) { throw new NoSuchElementException("id에 맞는 document가 없음"); } @@ -46,6 +50,14 @@ public Long addDocument(AddDocumentRequest addDocumentRequest) { return documentId; } + @Override + public OutBox findOutboxOf(Long drafterId) { + List elements = documentDao.findByDrafter(drafterId); + Documents documents = new Documents(elements); + + return OutBox.of(documents); + } + private void addDocumentApproval(AddDocumentApprovalParam addDocumentApprovalParam) { try { documentApprovalDao.addApprovals(addDocumentApprovalParam); diff --git a/src/main/java/playground/domain/document/dao/DocumentApprovalDao.java b/src/main/java/playground/domain/document/dao/DocumentApprovalDao.java index 0eb849e..a5759d3 100644 --- a/src/main/java/playground/domain/document/dao/DocumentApprovalDao.java +++ b/src/main/java/playground/domain/document/dao/DocumentApprovalDao.java @@ -1,6 +1,6 @@ package playground.domain.document.dao; -import playground.domain.document.dto.AddDocumentApprovalParam; +import playground.domain.document.dto.param.AddDocumentApprovalParam; public interface DocumentApprovalDao { diff --git a/src/main/java/playground/domain/document/dao/DocumentDao.java b/src/main/java/playground/domain/document/dao/DocumentDao.java index ddce873..fe16e85 100644 --- a/src/main/java/playground/domain/document/dao/DocumentDao.java +++ b/src/main/java/playground/domain/document/dao/DocumentDao.java @@ -1,11 +1,15 @@ package playground.domain.document.dao; -import playground.domain.document.dto.AddDocumentParam; +import playground.domain.document.dto.param.AddDocumentParam; import playground.domain.document.entity.Document; +import java.util.List; + public interface DocumentDao { Document findById(Long id); Long addDocument(AddDocumentParam addDocumentParam); + + List findByDrafter(Long drafterId); } diff --git a/src/main/java/playground/domain/document/dao/DocumentRowMapper.java b/src/main/java/playground/domain/document/dao/DocumentRowMapper.java index 1bdedf8..05433cc 100644 --- a/src/main/java/playground/domain/document/dao/DocumentRowMapper.java +++ b/src/main/java/playground/domain/document/dao/DocumentRowMapper.java @@ -24,6 +24,7 @@ public Document mapRow(ResultSet rs, int rowNum) throws SQLException { .build()) .category(Category.valueOf(rs.getString("category"))) .approvalState(ApprovalState.valueOf(rs.getString("approval_state"))) + .createdAt(rs.getTimestamp("created_at")) .build(); } } diff --git a/src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java b/src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java index 8ba7fd0..a52babd 100644 --- a/src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java +++ b/src/main/java/playground/domain/document/dao/h2/DocumentApprovalH2Dao.java @@ -7,8 +7,8 @@ import org.springframework.jdbc.core.simple.SimpleJdbcInsertOperations; import org.springframework.stereotype.Repository; import playground.domain.document.dao.DocumentApprovalDao; -import playground.domain.document.dto.AddDocumentApprovalParam; -import playground.domain.document.dto.sql.AddDocumentApprovalSqlParam; +import playground.domain.document.dto.param.AddDocumentApprovalParam; +import playground.domain.document.dto.param.sql.AddDocumentApprovalSqlParam; import java.util.List; diff --git a/src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java b/src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java index 5112617..e9f4456 100644 --- a/src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java +++ b/src/main/java/playground/domain/document/dao/h2/DocumentH2Dao.java @@ -8,10 +8,12 @@ import org.springframework.stereotype.Repository; import playground.domain.document.dao.DocumentDao; import playground.domain.document.dao.DocumentRowMapper; -import playground.domain.document.dto.AddDocumentParam; -import playground.domain.document.dto.sql.AddDocumentSqlParam; +import playground.domain.document.dto.param.AddDocumentParam; +import playground.domain.document.dto.param.sql.AddDocumentSqlParam; import playground.domain.document.entity.Document; +import java.util.List; + @Repository @RequiredArgsConstructor public class DocumentH2Dao implements DocumentDao { @@ -22,6 +24,7 @@ public class DocumentH2Dao implements DocumentDao { @Override public Document findById(Long id) throws EmptyResultDataAccessException { String query = "SELECT * FROM DOCUMENT WHERE ID=" + id; + return jdbcTemplate.queryForObject(query, documentRowMapper); } @@ -35,4 +38,11 @@ public Long addDocument(AddDocumentParam addDocumentParam) { .executeAndReturnKey(AddDocumentSqlParam.of(addDocumentParam)) .longValue(); } + + @Override + public List findByDrafter(Long drafterId) { + String query = "SELECT * FROM DOCUMENT WHERE DRAFTER_ID=" + drafterId; + + return jdbcTemplate.query(query, documentRowMapper); + } } diff --git a/src/main/java/playground/domain/document/dto/BoxDocument.java b/src/main/java/playground/domain/document/dto/BoxDocument.java new file mode 100644 index 0000000..d04a414 --- /dev/null +++ b/src/main/java/playground/domain/document/dto/BoxDocument.java @@ -0,0 +1,28 @@ +package playground.domain.document.dto; + +import lombok.Builder; +import lombok.Getter; +import playground.domain.document.entity.Document; + +@Getter +@Builder +public class BoxDocument { + + private final Long id; + private final String title; + private final String category; + private final String categoryText; + private final String approvalState; + private final String approvalStateText; + + public static BoxDocument of(Document document) { + return BoxDocument.builder() + .id(document.getId()) + .title(document.getTitle()) + .category(document.getCategory().name()) + .categoryText(document.getCategory().getText()) + .approvalState(document.getApprovalState().name()) + .approvalStateText(document.getApprovalState().getText()) + .build(); + } +} diff --git a/src/main/java/playground/domain/document/dto/OutBox.java b/src/main/java/playground/domain/document/dto/OutBox.java new file mode 100644 index 0000000..a6484d1 --- /dev/null +++ b/src/main/java/playground/domain/document/dto/OutBox.java @@ -0,0 +1,23 @@ +package playground.domain.document.dto; + +import lombok.Builder; +import lombok.Getter; +import playground.domain.document.entity.Documents; + +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@Builder +public class OutBox { + + private final List elements; + + public static OutBox of(Documents documents) { + return OutBox.builder() + .elements(documents.stream() + .map(BoxDocument::of) + .collect(Collectors.toList())) + .build(); + } +} diff --git a/src/main/java/playground/domain/document/dto/DocumentDto.java b/src/main/java/playground/domain/document/dto/SingleDocument.java similarity index 88% rename from src/main/java/playground/domain/document/dto/DocumentDto.java rename to src/main/java/playground/domain/document/dto/SingleDocument.java index 38adc59..6c097eb 100644 --- a/src/main/java/playground/domain/document/dto/DocumentDto.java +++ b/src/main/java/playground/domain/document/dto/SingleDocument.java @@ -9,7 +9,7 @@ @Getter @Builder @RequiredArgsConstructor -public class DocumentDto { +public class SingleDocument { private final Long id; private final String title; @@ -21,8 +21,8 @@ public class DocumentDto { private final String approvalState; private final String approvalStateText; - public static DocumentDto of(Document document, User drafter) { - return DocumentDto.builder() + public static SingleDocument of(Document document, User drafter) { + return SingleDocument.builder() .id(document.getId()) .title(document.getTitle()) .contents(document.getContents()) diff --git a/src/main/java/playground/domain/document/dto/AddDocumentApprovalParam.java b/src/main/java/playground/domain/document/dto/param/AddDocumentApprovalParam.java similarity index 85% rename from src/main/java/playground/domain/document/dto/AddDocumentApprovalParam.java rename to src/main/java/playground/domain/document/dto/param/AddDocumentApprovalParam.java index b391336..9306bc8 100644 --- a/src/main/java/playground/domain/document/dto/AddDocumentApprovalParam.java +++ b/src/main/java/playground/domain/document/dto/param/AddDocumentApprovalParam.java @@ -1,7 +1,8 @@ -package playground.domain.document.dto; +package playground.domain.document.dto.param; import lombok.Builder; import lombok.Getter; +import playground.domain.document.dto.AddDocumentRequest; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/playground/domain/document/dto/AddDocumentParam.java b/src/main/java/playground/domain/document/dto/param/AddDocumentParam.java similarity index 88% rename from src/main/java/playground/domain/document/dto/AddDocumentParam.java rename to src/main/java/playground/domain/document/dto/param/AddDocumentParam.java index d9952fe..efc080d 100644 --- a/src/main/java/playground/domain/document/dto/AddDocumentParam.java +++ b/src/main/java/playground/domain/document/dto/param/AddDocumentParam.java @@ -1,7 +1,8 @@ -package playground.domain.document.dto; +package playground.domain.document.dto.param; import lombok.Builder; import lombok.Getter; +import playground.domain.document.dto.AddDocumentRequest; import static playground.domain.document.entity.ApprovalState.DEFAULT_APPROVAL_STATE_TEXT; diff --git a/src/main/java/playground/domain/document/dto/sql/AddDocumentApprovalSqlParam.java b/src/main/java/playground/domain/document/dto/param/sql/AddDocumentApprovalSqlParam.java similarity index 92% rename from src/main/java/playground/domain/document/dto/sql/AddDocumentApprovalSqlParam.java rename to src/main/java/playground/domain/document/dto/param/sql/AddDocumentApprovalSqlParam.java index 9fb4796..595b13a 100644 --- a/src/main/java/playground/domain/document/dto/sql/AddDocumentApprovalSqlParam.java +++ b/src/main/java/playground/domain/document/dto/param/sql/AddDocumentApprovalSqlParam.java @@ -1,4 +1,4 @@ -package playground.domain.document.dto.sql; +package playground.domain.document.dto.param.sql; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; diff --git a/src/main/java/playground/domain/document/dto/sql/AddDocumentSqlParam.java b/src/main/java/playground/domain/document/dto/param/sql/AddDocumentSqlParam.java similarity index 74% rename from src/main/java/playground/domain/document/dto/sql/AddDocumentSqlParam.java rename to src/main/java/playground/domain/document/dto/param/sql/AddDocumentSqlParam.java index 484b8bc..f0a8c98 100644 --- a/src/main/java/playground/domain/document/dto/sql/AddDocumentSqlParam.java +++ b/src/main/java/playground/domain/document/dto/param/sql/AddDocumentSqlParam.java @@ -1,8 +1,10 @@ -package playground.domain.document.dto.sql; +package playground.domain.document.dto.param.sql; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import playground.domain.document.dto.AddDocumentParam; +import playground.domain.document.dto.param.AddDocumentParam; + +import java.time.LocalDateTime; public class AddDocumentSqlParam extends MapSqlParameterSource { @@ -12,6 +14,7 @@ public static SqlParameterSource of(AddDocumentParam addDocumentParam) { .addValue("contents", addDocumentParam.getContents()) .addValue("drafter_id", addDocumentParam.getDrafterId()) .addValue("category", addDocumentParam.getCategoryText()) - .addValue("approval_state", addDocumentParam.getApprovalStateText()); + .addValue("approval_state", addDocumentParam.getApprovalStateText()) + .addValue("created_at", LocalDateTime.now()); } } diff --git a/src/main/java/playground/domain/document/entity/Document.java b/src/main/java/playground/domain/document/entity/Document.java index 4566382..af21452 100644 --- a/src/main/java/playground/domain/document/entity/Document.java +++ b/src/main/java/playground/domain/document/entity/Document.java @@ -4,6 +4,7 @@ import lombok.Getter; import playground.domain.user.entity.User; +import java.sql.Timestamp; import java.util.Objects; @Builder @@ -16,6 +17,7 @@ public class Document { private User drafter; private Category category; private ApprovalState approvalState; + private Timestamp createdAt; // private DocumentApprovals approvals; @Override diff --git a/src/main/java/playground/domain/document/entity/Documents.java b/src/main/java/playground/domain/document/entity/Documents.java new file mode 100644 index 0000000..94221bc --- /dev/null +++ b/src/main/java/playground/domain/document/entity/Documents.java @@ -0,0 +1,29 @@ +package playground.domain.document.entity; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Stream; + +@Getter +public class Documents { + + private final List elements; + + public Documents(List elements) { + this.elements = Collections.unmodifiableList(sort(elements)); + } + + private List sort(List documents) { + List elements = new ArrayList<>(documents); + elements.sort(Comparator.comparing(Document::getCreatedAt).reversed()); + return elements; + } + + public Stream stream() { + return elements.stream(); + } +} diff --git a/src/main/java/playground/test.http b/src/main/java/playground/test.http index 22f97c0..8ff8c73 100644 --- a/src/main/java/playground/test.http +++ b/src/main/java/playground/test.http @@ -1,11 +1,4 @@ -GET http://localhost:8080/api/documents/4 - -HTTP/1.1 200 -Accept: application/json -Transfer-Encoding: chunked -Date: Tue, 24 Aug 2021 13:55:45 GMT -Keep-Alive: timeout=60 -Connection: keep-alive +GET http://localhost:8080/api/documents/1 ### POST http://localhost:8080/api/documents @@ -20,4 +13,7 @@ Content-Type: application/json 1, 2 ] -} \ No newline at end of file +} + +### +GET http://localhost:8080/api/documents/outbox?drafterId=1 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 51fca4a..40e7286 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -3,4 +3,7 @@ spring: driver-class-name: org.h2.Driver url: jdbc:h2:mem:db username: sa - password: \ No newline at end of file + password: + h2: + console: + enabled: true \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 58ea5f7..aee03be 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -4,8 +4,8 @@ values ('유저1'); insert into user(name) values ('유저2'); -insert into document(title, contents, drafter_id, category, approval_state) -values ('1번 문서', '내용1', 1, 'OPERATING_EXPENSES', 'DRAFTING'); +insert into document(title, contents, drafter_id, category, approval_state, created_at) +values ('1번 문서', '내용1', 1, 'OPERATING_EXPENSES', 'DRAFTING', '2021-10-23 23:59:59'); -insert into document(title, contents, drafter_id, category, approval_state) -values ('2번 문서', '내용2', 1, 'OPERATING_EXPENSES', 'APPROVED'); \ No newline at end of file +insert into document(title, contents, drafter_id, category, approval_state, created_at) +values ('2번 문서', '내용2', 1, 'OPERATING_EXPENSES', 'APPROVED', '2021-10-24 23:59:59'); \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index a312d3d..0191413 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -16,6 +16,7 @@ create table document drafter_id bigint not null, category varchar(255) not null, approval_state varchar(255) not null, + created_at timestamp not null, foreign key (drafter_id) references user (id) ); diff --git a/src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java b/src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java index a307aea..c17ae14 100644 --- a/src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java +++ b/src/test/java/playground/domain/document/dao/h2/DocumentApprovalH2DaoTest.java @@ -6,7 +6,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataIntegrityViolationException; import playground.domain.document.dao.DocumentApprovalDao; -import playground.domain.document.dto.AddDocumentApprovalParam; +import playground.domain.document.dto.param.AddDocumentApprovalParam; import java.util.Arrays; import java.util.Collections; diff --git a/src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java b/src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java index 81554bd..95ea60a 100644 --- a/src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java +++ b/src/test/java/playground/domain/document/dao/h2/DocumentH2DaoTest.java @@ -5,9 +5,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.EmptyResultDataAccessException; -import playground.domain.document.dto.AddDocumentParam; +import playground.domain.document.dto.param.AddDocumentParam; import playground.domain.document.entity.Document; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -61,4 +63,32 @@ void add_document_success() { // then assertThat(documentId).isNotNull(); } + + @Test + @DisplayName("작성자 ID로 문서를 찾아온다.") + void find_by_drafter_success() { + // given + Long drafterId = 1L; + + // when + List documents = documentH2Dao.findByDrafter(drafterId); + + // then + assertThat(documents) + .extracting("drafter.id") + .containsOnly(drafterId); + } + + @Test + @DisplayName("작성자가 작성한 문서가 없으면 빈 리스트를 반환한다.") + void find_by_drafter_success_empty() { + // given + Long drafterId = 2L; + + // when + List documents = documentH2Dao.findByDrafter(drafterId); + + // then + assertThat(documents).isEmpty(); + } } \ No newline at end of file