diff --git a/src/main/java/playground/controller/document/DocumentController.java b/src/main/java/playground/controller/document/DocumentController.java index 56deb57..68a4785 100644 --- a/src/main/java/playground/controller/document/DocumentController.java +++ b/src/main/java/playground/controller/document/DocumentController.java @@ -9,8 +9,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import playground.controller.document.request.CreateDocumentRequest; import playground.service.document.DocumentService; +import playground.service.document.request.CreateDocumentRequest; +import playground.service.document.response.SelectCategoryResponse; import playground.service.document.response.SelectDocumentResponse; import playground.service.document.response.SelectSingleOutBoxResponse; @@ -29,19 +30,25 @@ public DocumentController(final DocumentService documentService) { @PostMapping public ResponseEntity create(@RequestBody @Valid final CreateDocumentRequest createDocumentRequest) { - documentService.save(createDocumentRequest); + documentService.create(createDocumentRequest); return new ResponseEntity<>(HttpStatus.CREATED); } @GetMapping("/{documentId}") - public ResponseEntity select(final @PathVariable Long documentId) { - SelectDocumentResponse selectDocumentResponse = documentService.select(documentId); + public ResponseEntity find(final @PathVariable Long documentId) { + SelectDocumentResponse selectDocumentResponse = documentService.find(documentId); return ResponseEntity.ok(selectDocumentResponse); } @GetMapping("/outbox") - public ResponseEntity> selectOutBox(final @RequestParam Long drafterId) { - List selectMultiOutBoxResponse = documentService.selectOutBox(drafterId); + public ResponseEntity> findOutBox(final @RequestParam Long drafterId) { + List selectMultiOutBoxResponse = documentService.findOutBox(drafterId); return ResponseEntity.ok(selectMultiOutBoxResponse); } + + @GetMapping("/categories") + public ResponseEntity> findCategories() { + List selectCategoryResponses = documentService.findCategories(); + return ResponseEntity.ok(selectCategoryResponses); + } } diff --git a/src/main/java/playground/controller/team/TeamController.java b/src/main/java/playground/controller/team/TeamController.java new file mode 100644 index 0000000..f1024c4 --- /dev/null +++ b/src/main/java/playground/controller/team/TeamController.java @@ -0,0 +1,38 @@ +package playground.controller.team; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import playground.service.team.TeamService; +import playground.service.team.request.CreateTeamRequest; +import playground.service.team.response.SelectTeamResponse; + +import javax.validation.Valid; +import java.util.List; + +@RestController +@RequestMapping(path = "/api/teams") +public class TeamController { + + private final TeamService teamService; + + public TeamController(final TeamService teamService) { + this.teamService = teamService; + } + + @PostMapping + public ResponseEntity create(@RequestBody @Valid final CreateTeamRequest createTeamRequest) { + teamService.create(createTeamRequest); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @GetMapping + public ResponseEntity> findAll() { + List selectTeamResponses = teamService.findAll(); + return ResponseEntity.ok(selectTeamResponses); + } +} diff --git a/src/main/java/playground/controller/user/UserController.java b/src/main/java/playground/controller/user/UserController.java index e6685f1..2e93f99 100644 --- a/src/main/java/playground/controller/user/UserController.java +++ b/src/main/java/playground/controller/user/UserController.java @@ -2,17 +2,21 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import playground.controller.user.request.CreateUserRequest; import playground.service.user.UserService; +import playground.service.user.request.CreateUserRequest; +import playground.service.user.response.SelectUserResponse; import javax.validation.Valid; +import java.util.List; @RestController -@RequestMapping(path = "/api/user") +@RequestMapping(path = "/api/users") public class UserController { private final UserService userService; @@ -23,7 +27,13 @@ public UserController(final UserService userService) { @PostMapping public ResponseEntity create(@RequestBody @Valid final CreateUserRequest createUserRequest) { - userService.save(createUserRequest); + userService.create(createUserRequest); return new ResponseEntity<>(HttpStatus.CREATED); } + + @GetMapping() + public ResponseEntity> findAllUserInTeam(@RequestParam final Long teamId) { + List selectUserResponses = userService.findAllUserInTeam(teamId); + return ResponseEntity.ok(selectUserResponses); + } } diff --git a/src/main/java/playground/domain/document/Document.java b/src/main/java/playground/domain/document/Document.java index d4fb7ac..4324084 100644 --- a/src/main/java/playground/domain/document/Document.java +++ b/src/main/java/playground/domain/document/Document.java @@ -1,51 +1,77 @@ package playground.domain.document; +import lombok.AccessLevel; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.document.vo.ApprovalState; +import playground.domain.document.vo.Category; +import playground.domain.user.User; -import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import java.util.Collections; +import java.util.List; +@Entity @Getter +@EqualsAndHashCode(of = "id") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "document") public class Document { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "document_id") private Long id; + + @Column(name = "title", nullable = false) private String title; + + @Enumerated(value = EnumType.STRING) + @Column(name = "category", nullable = false) private Category category; + + @Column(name = "contents", nullable = false) private String contents; - private Long drafterId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "drafter_id") + private User drafter; + + @Embedded + private DocumentApprovals documentApprovals = new DocumentApprovals(); + + @Enumerated(value = EnumType.STRING) + @Column(name = "approval_state", nullable = false) private ApprovalState approvalState; @Builder - private Document(final String title, final String category, - final String contents, final Long drafterId) { + private Document(final String title, final Category category, + final String contents, final User drafter) { this.title = title; - this.category = Category.valueOf(category); + this.category = category; this.contents = contents; - this.drafterId = drafterId; + this.drafter = drafter; this.approvalState = ApprovalState.DRAFTING; } - @Builder(builderMethodName = "builderForDao", builderClassName = "BuilderForDao") - private Document(final Long id, final String title, final String category, - final String contents, final Long drafterId, final String approvalState) { - this.id = id; - this.title = title; - this.category = Category.valueOf(category); - this.contents = contents; - this.drafterId = drafterId; - this.approvalState = ApprovalState.valueOf(approvalState); - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final Document document = (Document) o; - return Objects.equals(id, document.id); + public void enrollApprovals(final List approvers) { + documentApprovals.enroll(approvers, this); } - @Override - public int hashCode() { - return Objects.hash(id); + public List getDocumentApprovals() { + return Collections.unmodifiableList(documentApprovals.getDocumentApprovals()); } } diff --git a/src/main/java/playground/domain/document/DocumentApproval.java b/src/main/java/playground/domain/document/DocumentApproval.java index f7f6545..f3f0b36 100644 --- a/src/main/java/playground/domain/document/DocumentApproval.java +++ b/src/main/java/playground/domain/document/DocumentApproval.java @@ -1,29 +1,68 @@ package playground.domain.document; +import lombok.AccessLevel; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.document.vo.ApprovalState; +import playground.domain.user.User; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +@Entity @Getter +@EqualsAndHashCode(of = "id") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "document_approval") public class DocumentApproval { - private Long approverId; - private Long documentId; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "document_approval_id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "approver_id") + private User approver; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "document_id") + private Document document; + + @Enumerated(value = EnumType.STRING) + @Column(name = "approval_state", nullable = false) private ApprovalState approvalState; + + @Column(name = "approval_order", nullable = false) private Integer approvalOrder; + + @Column(name = "approval_comment") private String approvalComment; @Builder - private DocumentApproval(final Long approverId, final Long documentId, final String approvalState, + private DocumentApproval(final User approver, final Document document, final ApprovalState approvalState, final Integer approvalOrder, final String approvalComment) { - this.approverId = approverId; - this.documentId = documentId; - this.approvalState = ApprovalState.valueOf(approvalState); + this.approver = approver; + this.document = document; + this.approvalState = approvalState; this.approvalOrder = approvalOrder; this.approvalComment = approvalComment; } - public static DocumentApproval of(final Long approverId, final Long documentId, final int approvalOrder) { - return new DocumentApproval(approverId, documentId, ApprovalState.DRAFTING.name(), + public static DocumentApproval of(final User approver, final Document document, final int approvalOrder) { + return new DocumentApproval(approver, document, ApprovalState.DRAFTING, approvalOrder, null); } } diff --git a/src/main/java/playground/domain/document/DocumentApprovals.java b/src/main/java/playground/domain/document/DocumentApprovals.java new file mode 100644 index 0000000..9e782fe --- /dev/null +++ b/src/main/java/playground/domain/document/DocumentApprovals.java @@ -0,0 +1,38 @@ +package playground.domain.document; + +import lombok.Getter; +import playground.domain.user.User; + +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.OneToMany; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +@Embeddable +public class DocumentApprovals { + + @OneToMany(mappedBy = "document", cascade = CascadeType.ALL, orphanRemoval = true) + private List documentApprovals = new ArrayList<>(); + + public void enroll(final List approvers, final Document document) { + checkEmpty(); + + for (int i = 0; i < approvers.size(); i++) { + DocumentApproval documentApproval = DocumentApproval.of(approvers.get(i), document, i + 1); + documentApprovals.add(documentApproval); + } + } + + private void checkEmpty() { + if (!documentApprovals.isEmpty()) { + throw new IllegalStateException("결재자 추가 등록이 불가능합니다."); + } + } + + public List getDocumentApprovals() { + return Collections.unmodifiableList(documentApprovals); + } +} diff --git a/src/main/java/playground/domain/document/ApprovalState.java b/src/main/java/playground/domain/document/vo/ApprovalState.java similarity index 85% rename from src/main/java/playground/domain/document/ApprovalState.java rename to src/main/java/playground/domain/document/vo/ApprovalState.java index 15be5e0..4b28acb 100644 --- a/src/main/java/playground/domain/document/ApprovalState.java +++ b/src/main/java/playground/domain/document/vo/ApprovalState.java @@ -1,4 +1,4 @@ -package playground.domain.document; +package playground.domain.document.vo; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/playground/domain/document/Category.java b/src/main/java/playground/domain/document/vo/Category.java similarity index 86% rename from src/main/java/playground/domain/document/Category.java rename to src/main/java/playground/domain/document/vo/Category.java index a68b3a1..24ea49b 100644 --- a/src/main/java/playground/domain/document/Category.java +++ b/src/main/java/playground/domain/document/vo/Category.java @@ -1,4 +1,4 @@ -package playground.domain.document; +package playground.domain.document.vo; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/playground/domain/team/Team.java b/src/main/java/playground/domain/team/Team.java new file mode 100644 index 0000000..bda4abe --- /dev/null +++ b/src/main/java/playground/domain/team/Team.java @@ -0,0 +1,45 @@ +package playground.domain.team; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.user.User; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "team", uniqueConstraints = { + @UniqueConstraint(name = "team_name_contraint", columnNames = "name") +}) +public class Team { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "team_id") + private Long id; + + @Column(name = "name", nullable = false) + private String name; + + @OneToMany(mappedBy = "team") + private List users = new ArrayList<>(); + + public Team(final String name) { + this.name = name; + } + + public void addUser(final User user) { + this.users.add(user); + } +} diff --git a/src/main/java/playground/domain/user/User.java b/src/main/java/playground/domain/user/User.java index 731d2e7..4ac6819 100644 --- a/src/main/java/playground/domain/user/User.java +++ b/src/main/java/playground/domain/user/User.java @@ -1,43 +1,66 @@ package playground.domain.user; +import lombok.AccessLevel; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.team.Team; +import playground.domain.user.vo.JobPosition; -import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +@Entity @Getter +@EqualsAndHashCode(of = "id") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "user", uniqueConstraints = { + @UniqueConstraint(name = "user_email_constraint", columnNames = "email") +}) public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_id") private Long id; + + @Column(name = "email", nullable = false) private String email; + + @Column(name = "password", nullable = false) private String password; + + @Column(name = "name", nullable = false) private String name; - public User(final String email, final String password, final String name) { - this.email = email; - this.password = password; - this.name = name; - } + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "team_id") + private Team team; + + @Enumerated(EnumType.STRING) + @Column(name = "job_position", nullable = false) + private JobPosition jobPosition; - @Builder(builderMethodName = "builderForDao") - public User(final Long id, final String email, final String password, final String name) { - this.id = id; + @Builder + public User(final String email, final String password, final String name, + final Team team, final JobPosition jobPosition) { this.email = email; this.password = password; this.name = name; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final User user = (User) o; - return Objects.equals(id, user.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); + this.team = team; + this.team.addUser(this); + this.jobPosition = jobPosition; } } diff --git a/src/main/java/playground/domain/user/vo/JobPosition.java b/src/main/java/playground/domain/user/vo/JobPosition.java new file mode 100644 index 0000000..e519b06 --- /dev/null +++ b/src/main/java/playground/domain/user/vo/JobPosition.java @@ -0,0 +1,15 @@ +package playground.domain.user.vo; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum JobPosition { + + TEAM_LEADER("팀장"), + PART_MANAGER("파트장"), + TEAM_MEMBER("팀원"); + + private final String text; +} diff --git a/src/main/java/playground/repository/document/DocumentApprovalRepository.java b/src/main/java/playground/repository/document/DocumentApprovalRepository.java index f08ece9..acbf3f7 100644 --- a/src/main/java/playground/repository/document/DocumentApprovalRepository.java +++ b/src/main/java/playground/repository/document/DocumentApprovalRepository.java @@ -1,56 +1,14 @@ package playground.repository.document; -import org.springframework.jdbc.core.BatchPreparedStatementSetter; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Component; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import playground.domain.document.DocumentApproval; -import javax.sql.DataSource; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.List; -@Component -public class DocumentApprovalRepository { +public interface DocumentApprovalRepository extends JpaRepository { - private final JdbcTemplate jdbcTemplate; - - public DocumentApprovalRepository(final DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } - - public void saveAll(final List documentApprovals) { - String saveQuery = "insert into document_approval (approver_id, document_id, approval_state, approval_order, approval_comment) " + - "values(?, ?, ?, ?, ?)"; - - jdbcTemplate.batchUpdate(saveQuery, new BatchPreparedStatementSetter() { - @Override - public void setValues(final PreparedStatement ps, final int i) throws SQLException { - DocumentApproval documentApproval = documentApprovals.get(i); - ps.setLong(1, documentApproval.getApproverId()); - ps.setLong(2, documentApproval.getDocumentId()); - ps.setString(3, documentApproval.getApprovalState().name()); - ps.setInt(4, documentApproval.getApprovalOrder()); - ps.setString(5, documentApproval.getApprovalComment()); - } - - @Override - public int getBatchSize() { - return documentApprovals.size(); - } - }); - } - - public List findAll() { - String selectAllQuery = "select * from document_approval"; - - return jdbcTemplate.query(selectAllQuery, (rs, rowNum) -> - DocumentApproval.builder() - .approverId(rs.getLong("approver_id")) - .approverId(rs.getLong("document_id")) - .approvalState(rs.getString("approval_state")) - .approvalOrder(rs.getInt("approval_order")) - .approvalComment(rs.getString("approval_comment")) - .build()); - } + @Query("select d from DocumentApproval d join fetch d.approver a join fetch a.team where d.id in :documentApprovalIds") + List findAllDocumentApprovalAndApproverAndTeamByIds(@Param("documentApprovalIds") List documentApprovalIds); } diff --git a/src/main/java/playground/repository/document/DocumentRepository.java b/src/main/java/playground/repository/document/DocumentRepository.java index 8c26ad9..720ae4c 100644 --- a/src/main/java/playground/repository/document/DocumentRepository.java +++ b/src/main/java/playground/repository/document/DocumentRepository.java @@ -1,75 +1,23 @@ package playground.repository.document; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Component; -import playground.domain.document.ApprovalState; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import playground.domain.document.Document; +import playground.domain.document.vo.ApprovalState; +import playground.service.document.response.SelectCategoryResponse; -import javax.sql.DataSource; -import java.sql.PreparedStatement; -import java.sql.Statement; import java.util.List; +import java.util.Optional; -@Component -public class DocumentRepository { +public interface DocumentRepository extends JpaRepository { - private final JdbcTemplate jdbcTemplate; + @Query("select d from Document d join fetch d.drafter join fetch d.documentApprovals.documentApprovals where d.id = :documentId") + Optional findDocumentAndDrafterAndDocumentApprovalsById(@Param("documentId") final Long documentId); - public DocumentRepository(final DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + @Query("select d from Document d join fetch d.drafter where d.drafter.id = :documentId and d.approvalState = :approvalState") + List findAllDocumentAndDrafterByDrafterIdAndApprovalState(@Param("documentId") final Long drafterId, @Param("approvalState") final ApprovalState approvalState); - public Long save(final Document document) { - String saveQuery = "insert into document (title, category, contents, drafter_id, approval_state) " + - "values (?, ?, ?, ?, ?)"; - KeyHolder keyHolder = new GeneratedKeyHolder(); - - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection - .prepareStatement(saveQuery, Statement.RETURN_GENERATED_KEYS); - ps.setString(1, document.getTitle()); - ps.setString(2, document.getCategory().name()); - ps.setString(3, document.getContents()); - ps.setLong(4, document.getDrafterId()); - ps.setString(5, document.getApprovalState().name()); - return ps; - }, keyHolder); - - return keyHolder.getKey().longValue(); - } - - public Document findById(final Long documentId) { - String selectQuery = "select * from document where id = ?"; - - try { - return jdbcTemplate.queryForObject(selectQuery, (rs, rowNum) -> - Document.builderForDao() - .id(rs.getLong("id")) - .title(rs.getString("title")) - .category(rs.getString("category")) - .contents(rs.getString("contents")) - .drafterId(rs.getLong("drafter_id")) - .approvalState(rs.getString("approval_state")) - .build(), documentId); - } catch (EmptyResultDataAccessException e) { - throw new IllegalArgumentException(String.format("[%d] 번호에 해당하는 문서가 존재하지 않습니다.", documentId)); - } - } - - public List findAllByDrafterIdAndApprovalState(final Long drafterId, final ApprovalState approvalState) { - String selectQuery = "select * from document where drafter_id = ? and approval_state = ? order by id desc"; - - return jdbcTemplate.query(selectQuery, (rs, rowNum) -> - Document.builderForDao() - .id(rs.getLong("id")) - .title(rs.getString("title")) - .category(rs.getString("category")) - .contents(rs.getString("contents")) - .drafterId(rs.getLong("drafter_id")) - .approvalState(rs.getString("approval_state")) - .build(), drafterId, approvalState.name()); - } + @Query("select distinct new playground.service.document.response.SelectCategoryResponse(d.category) from Document d") + List findCategories(); } diff --git a/src/main/java/playground/repository/team/TeamRepository.java b/src/main/java/playground/repository/team/TeamRepository.java new file mode 100644 index 0000000..439ff5c --- /dev/null +++ b/src/main/java/playground/repository/team/TeamRepository.java @@ -0,0 +1,7 @@ +package playground.repository.team; + +import org.springframework.data.jpa.repository.JpaRepository; +import playground.domain.team.Team; + +public interface TeamRepository extends JpaRepository { +} diff --git a/src/main/java/playground/repository/user/UserRepository.java b/src/main/java/playground/repository/user/UserRepository.java index 05d9895..68a1249 100644 --- a/src/main/java/playground/repository/user/UserRepository.java +++ b/src/main/java/playground/repository/user/UserRepository.java @@ -1,69 +1,7 @@ package playground.repository.user; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Component; +import org.springframework.data.jpa.repository.JpaRepository; import playground.domain.user.User; -import javax.sql.DataSource; -import java.sql.PreparedStatement; -import java.sql.Statement; -import java.util.Collections; -import java.util.List; - -@Component -public class UserRepository { - - private JdbcTemplate jdbcTemplate; - - public UserRepository(final DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } - - public Long save(final User user) { - String saveQuery = "insert into user (email, password, name) values (?, ?, ?)"; - KeyHolder keyHolder = new GeneratedKeyHolder(); - - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection - .prepareStatement(saveQuery, Statement.RETURN_GENERATED_KEYS); - ps.setString(1, user.getEmail()); - ps.setString(2, user.getPassword()); - ps.setString(3, user.getName()); - return ps; - }, keyHolder); - - return keyHolder.getKey().longValue(); - } - - public List findAllById(final List userIds) { - String inSql = String.join(",", Collections.nCopies(userIds.size(), "?")); - String selectQuery = String.format("select * from user where id in (%s)", inSql); - - return jdbcTemplate.query(selectQuery, (rs, rowNum) -> - User.builderForDao() - .id(rs.getLong("id")) - .email(rs.getString("email")) - .password(rs.getString("password")) - .name(rs.getString("name")) - .build(), userIds.toArray()); - } - - public User findById(final Long userId) { - String selectQuery = "select * from user where id = ?"; - try { - return jdbcTemplate.queryForObject(selectQuery, (rs, rowNum) -> - User.builderForDao() - .id(rs.getLong("id")) - .email(rs.getString("email")) - .password(rs.getString("password")) - .name(rs.getString("name")) - .build(), userId); - - } catch (EmptyResultDataAccessException e) { - throw new IllegalArgumentException(String.format("[%d] 번호에 해당하는 회원이 존재하지 않습니다.", userId)); - } - } +public interface UserRepository extends JpaRepository { } diff --git a/src/main/java/playground/service/document/DocumentService.java b/src/main/java/playground/service/document/DocumentService.java index 6a37a91..7a400bd 100644 --- a/src/main/java/playground/service/document/DocumentService.java +++ b/src/main/java/playground/service/document/DocumentService.java @@ -2,18 +2,19 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import playground.controller.document.request.CreateDocumentRequest; -import playground.domain.document.ApprovalState; import playground.domain.document.Document; import playground.domain.document.DocumentApproval; +import playground.domain.document.vo.ApprovalState; import playground.domain.user.User; import playground.repository.document.DocumentApprovalRepository; import playground.repository.document.DocumentRepository; +import playground.service.document.request.CreateDocumentRequest; +import playground.service.document.response.SelectCategoryResponse; import playground.service.document.response.SelectDocumentResponse; import playground.service.document.response.SelectSingleOutBoxResponse; import playground.service.user.UserService; -import java.util.ArrayList; +import javax.persistence.EntityNotFoundException; import java.util.List; import java.util.stream.Collectors; @@ -33,45 +34,54 @@ public DocumentService(final DocumentRepository documentRepository, } @Transactional - public void save(final CreateDocumentRequest createDocumentRequest) { - Long drafterId = createDocumentRequest.getDrafterId(); + public void create(final CreateDocumentRequest createDocumentRequest) { List approverIds = createDocumentRequest.getApproverIds(); - checkUserExistence(drafterId, approverIds); - - Document document = createDocumentRequest.toDocument(); - Long documentId = documentRepository.save(document); + checkApprovalExistence(approverIds); - List documentApprovals = createDocumentRequest.toDocumentApprovals(documentId); - documentApprovalRepository.saveAll(documentApprovals); + Long drafterId = createDocumentRequest.getDrafterId(); + User drafter = userService.findById(drafterId); + Document document = createDocumentRequest.toDocument(drafter); + List approvers = findAllUserById(approverIds); + document.enrollApprovals(approvers); + documentRepository.save(document); } - private void checkUserExistence(final Long drafterId, final List approvalIds) { - ArrayList userIds = new ArrayList<>(); + private void checkApprovalExistence(final List approvalIds) { + List users = userService.findAllById(approvalIds); - if (!approvalIds.contains(drafterId)) { - userIds.add(drafterId); + if (approvalIds.size() != users.size()) { + throw new EntityNotFoundException(String.format("%s 식별번호와 일치하는 결재자를 모두 찾지 못했습니다.", approvalIds)); } + } - userIds.addAll(approvalIds); - - List users = userService.findAllById(userIds); - - if (userIds.size() != users.size()) { - throw new IllegalArgumentException("전달받은 회원 식별자와 일치하는 회원을 모두 찾지 못했습니다."); - } + private List findAllUserById(final List approverIds) { + return approverIds.stream() + .map(userService::findById) + .collect(Collectors.toList()); } @Transactional(readOnly = true) - public SelectDocumentResponse select(final Long documentId) { - Document document = documentRepository.findById(documentId); - User user = userService.findById(document.getDrafterId()); + public SelectDocumentResponse find(final Long documentId) { + Document document = findDocumentAndDrafterAndDocumentApprovalsById(documentId); - return new SelectDocumentResponse(document, user); + List documentApprovalIds = document.getDocumentApprovals() + .stream() + .map(DocumentApproval::getId) + .collect(Collectors.toList()); + + List documentApprovals = documentApprovalRepository.findAllDocumentApprovalAndApproverAndTeamByIds(documentApprovalIds); + + return new SelectDocumentResponse(document, documentApprovals); + } + + private Document findDocumentAndDrafterAndDocumentApprovalsById(final Long documentId) { + return documentRepository.findDocumentAndDrafterAndDocumentApprovalsById(documentId) + .orElseThrow(() -> new EntityNotFoundException(String.format("[%d] 식별번호에 해당하는 문서가 존재하지 않습니다.", documentId))); } @Transactional(readOnly = true) - public List selectOutBox(final Long drafterId) { - List documents = documentRepository.findAllByDrafterIdAndApprovalState(drafterId, ApprovalState.DRAFTING); + public List findOutBox(final Long drafterId) { + List documents = documentRepository.findAllDocumentAndDrafterByDrafterIdAndApprovalState(drafterId, ApprovalState.DRAFTING); checkEmpty(documents); return documents.stream() @@ -84,4 +94,8 @@ private void checkEmpty(final List documents) { throw new IllegalArgumentException("현재 결재중인 문서가 존재하지 않습니다."); } } + + public List findCategories() { + return documentRepository.findCategories(); + } } diff --git a/src/main/java/playground/controller/document/request/CreateDocumentRequest.java b/src/main/java/playground/service/document/request/CreateDocumentRequest.java similarity index 60% rename from src/main/java/playground/controller/document/request/CreateDocumentRequest.java rename to src/main/java/playground/service/document/request/CreateDocumentRequest.java index 475409b..ce715a8 100644 --- a/src/main/java/playground/controller/document/request/CreateDocumentRequest.java +++ b/src/main/java/playground/service/document/request/CreateDocumentRequest.java @@ -1,16 +1,15 @@ -package playground.controller.document.request; +package playground.service.document.request; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import playground.domain.document.Document; -import playground.domain.document.DocumentApproval; +import playground.domain.document.vo.Category; +import playground.domain.user.User; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; @Getter @@ -41,23 +40,12 @@ public CreateDocumentRequest(final String title, final String category, final St this.approverIds = approverIds; } - public Document toDocument() { + public Document toDocument(final User drafter) { return Document.builder() .title(title) - .category(category) + .category(Category.valueOf(category)) .contents(contents) - .drafterId(drafterId) + .drafter(drafter) .build(); } - - public List toDocumentApprovals(final Long documentId) { - ArrayList documentApprovals = new ArrayList<>(); - - for (int i = 0; i < approverIds.size(); i++) { - DocumentApproval documentApproval = DocumentApproval.of(approverIds.get(i), documentId, i); - documentApprovals.add(documentApproval); - } - - return Collections.unmodifiableList(documentApprovals); - } } diff --git a/src/main/java/playground/service/document/response/SelectApproverResponse.java b/src/main/java/playground/service/document/response/SelectApproverResponse.java new file mode 100644 index 0000000..9fc8811 --- /dev/null +++ b/src/main/java/playground/service/document/response/SelectApproverResponse.java @@ -0,0 +1,29 @@ +package playground.service.document.response; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.document.DocumentApproval; +import playground.domain.document.vo.ApprovalState; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SelectApproverResponse { + + private String approverTeamName; + private String approverName; + private ApprovalState approvalState; + private Integer approvalOrder; + private String approvalComment; + private String approvalStateText; + + public SelectApproverResponse(final DocumentApproval documentApproval) { + this.approverTeamName = documentApproval.getApprover().getTeam().getName(); + this.approverName = documentApproval.getApprover().getName(); + ; + this.approvalState = documentApproval.getApprovalState(); + this.approvalOrder = documentApproval.getApprovalOrder(); + this.approvalComment = documentApproval.getApprovalComment(); + this.approvalStateText = documentApproval.getApprovalState().getText(); + } +} diff --git a/src/main/java/playground/service/document/response/SelectCategoryResponse.java b/src/main/java/playground/service/document/response/SelectCategoryResponse.java new file mode 100644 index 0000000..b805f5d --- /dev/null +++ b/src/main/java/playground/service/document/response/SelectCategoryResponse.java @@ -0,0 +1,23 @@ +package playground.service.document.response; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import playground.domain.document.vo.Category; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SelectCategoryResponse { + + private Category category; + + public SelectCategoryResponse(final Category category) { + this.category = category; + } + + public String getValue() { + return category.name(); + } + + public String getText() { + return category.getText(); + } +} diff --git a/src/main/java/playground/service/document/response/SelectDocumentResponse.java b/src/main/java/playground/service/document/response/SelectDocumentResponse.java index be07618..c5f87bf 100644 --- a/src/main/java/playground/service/document/response/SelectDocumentResponse.java +++ b/src/main/java/playground/service/document/response/SelectDocumentResponse.java @@ -4,7 +4,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; import playground.domain.document.Document; -import playground.domain.user.User; +import playground.domain.document.DocumentApproval; +import playground.domain.document.vo.ApprovalState; +import playground.domain.document.vo.Category; +import playground.service.user.response.SelectUserResponse; + +import java.util.List; +import java.util.stream.Collectors; @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -12,22 +18,24 @@ public class SelectDocumentResponse { private Long id; private String title; - private String category; + private Category category; private String contents; - private Long userId; - private String approvalState; - private String userName; + private ApprovalState approvalState; + private SelectUserResponse drafter; + private List approvers; private String categoryText; private String approvalStateText; - public SelectDocumentResponse(final Document document, final User user) { + public SelectDocumentResponse(final Document document, final List documentApprovals) { this.id = document.getId(); this.title = document.getTitle(); - this.category = document.getCategory().name(); + this.category = document.getCategory(); this.contents = document.getContents(); - this.userId = user.getId(); - this.approvalState = document.getApprovalState().name(); - this.userName = user.getName(); + this.approvalState = document.getApprovalState(); + this.drafter = new SelectUserResponse(document.getDrafter()); + this.approvers = documentApprovals.stream() + .map(SelectApproverResponse::new) + .collect(Collectors.toList()); this.categoryText = document.getCategory().getText(); this.approvalStateText = document.getApprovalState().getText(); } diff --git a/src/main/java/playground/service/document/response/SelectSingleOutBoxResponse.java b/src/main/java/playground/service/document/response/SelectSingleOutBoxResponse.java index a9b6bc9..e486493 100644 --- a/src/main/java/playground/service/document/response/SelectSingleOutBoxResponse.java +++ b/src/main/java/playground/service/document/response/SelectSingleOutBoxResponse.java @@ -4,6 +4,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import playground.domain.document.Document; +import playground.domain.document.vo.ApprovalState; +import playground.domain.document.vo.Category; @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -11,16 +13,16 @@ public class SelectSingleOutBoxResponse { private Long id; private String title; - private String category; - private String approvalState; + private Category category; + private ApprovalState approvalState; private String categoryText; private String approvalStateText; public SelectSingleOutBoxResponse(final Document document) { this.id = document.getId(); this.title = document.getTitle(); - this.category = document.getCategory().name(); - this.approvalState = document.getApprovalState().name(); + this.category = document.getCategory(); + this.approvalState = document.getApprovalState(); this.categoryText = document.getCategory().getText(); this.approvalStateText = document.getApprovalState().getText(); } diff --git a/src/main/java/playground/service/team/TeamService.java b/src/main/java/playground/service/team/TeamService.java new file mode 100644 index 0000000..2511681 --- /dev/null +++ b/src/main/java/playground/service/team/TeamService.java @@ -0,0 +1,59 @@ +package playground.service.team; + +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import playground.domain.team.Team; +import playground.repository.team.TeamRepository; +import playground.service.team.request.CreateTeamRequest; +import playground.service.team.response.SelectTeamResponse; + +import javax.persistence.EntityNotFoundException; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class TeamService { + + private final TeamRepository teamRepository; + + public TeamService(final TeamRepository teamRepository) { + this.teamRepository = teamRepository; + } + + @Transactional + public void create(final CreateTeamRequest createTeamRequest) { + Team team = createTeamRequest.toTeam(); + save(team); + } + + private void save(final Team team) { + try { + teamRepository.save(team); + } catch (DataIntegrityViolationException e) { + throw new IllegalArgumentException(String.format("[%s] 이미 존재하는 팀입니다.", team.getName())); + } + } + + @Transactional(readOnly = true) + public List findAll() { + List teams = teamRepository.findAll(); + checkEmpty(teams); + + return teams.stream() + .map(SelectTeamResponse::new) + .collect(Collectors.toList()); + } + + private void checkEmpty(final List teams) { + if (teams.isEmpty()) { + throw new EntityNotFoundException("존재하는 팀이 없습니다."); + } + } + + @Transactional(readOnly = true) + public Team findById(final Long teamId) { + return teamRepository.findById(teamId) + .orElseThrow(() -> new EntityNotFoundException(String.format("[%d] 식별번호에 해당하는 팀이 존재하지 않습니다.", teamId))); + } +} diff --git a/src/main/java/playground/service/team/request/CreateTeamRequest.java b/src/main/java/playground/service/team/request/CreateTeamRequest.java new file mode 100644 index 0000000..5030bb6 --- /dev/null +++ b/src/main/java/playground/service/team/request/CreateTeamRequest.java @@ -0,0 +1,25 @@ +package playground.service.team.request; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.team.Team; + +import javax.validation.constraints.NotBlank; + + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CreateTeamRequest { + + @NotBlank + private String name; + + public CreateTeamRequest(final String name) { + this.name = name; + } + + public Team toTeam() { + return new Team(name); + } +} diff --git a/src/main/java/playground/service/team/response/SelectTeamResponse.java b/src/main/java/playground/service/team/response/SelectTeamResponse.java new file mode 100644 index 0000000..c9bf101 --- /dev/null +++ b/src/main/java/playground/service/team/response/SelectTeamResponse.java @@ -0,0 +1,19 @@ +package playground.service.team.response; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.team.Team; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SelectTeamResponse { + + private Long id; + private String name; + + public SelectTeamResponse(final Team team) { + this.id = team.getId(); + this.name = team.getName(); + } +} diff --git a/src/main/java/playground/service/user/UserService.java b/src/main/java/playground/service/user/UserService.java index b32ce3b..20b7771 100644 --- a/src/main/java/playground/service/user/UserService.java +++ b/src/main/java/playground/service/user/UserService.java @@ -1,44 +1,71 @@ package playground.service.user; -import org.springframework.dao.DuplicateKeyException; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import playground.controller.user.request.CreateUserRequest; +import playground.domain.team.Team; import playground.domain.user.User; import playground.repository.user.UserRepository; +import playground.service.team.TeamService; +import playground.service.user.request.CreateUserRequest; +import playground.service.user.response.SelectUserResponse; +import javax.persistence.EntityNotFoundException; import java.util.List; +import java.util.stream.Collectors; @Service public class UserService { private final UserRepository userRepository; + private final TeamService teamService; - public UserService(final UserRepository userRepository) { + public UserService(final UserRepository userRepository, final TeamService teamService) { this.userRepository = userRepository; + this.teamService = teamService; } @Transactional - public void save(final CreateUserRequest createUserRequest) { - User user = createUserRequest.toUser(); - saveInDatabase(user); + public void create(final CreateUserRequest createUserRequest) { + Team team = teamService.findById(createUserRequest.getTeamId()); + User user = createUserRequest.toUser(team); + save(user); } - private void saveInDatabase(final User user) { + private void save(final User user) { try { userRepository.save(user); - } catch (DuplicateKeyException e) { + } catch (DataIntegrityViolationException e) { throw new IllegalArgumentException(String.format("[%s] 이미 가입된 이메일입니다.", user.getEmail())); } } @Transactional(readOnly = true) public List findAllById(final List userIds) { - return userRepository.findAllById(userIds); + List users = userRepository.findAllById(userIds); + checkEmpty(userIds, users); + + return users; + } + + private void checkEmpty(final List userIds, final List users) { + if (users.isEmpty()) { + throw new EntityNotFoundException(String.format("%s 식별번호에 해당하는 회원이 존재하지 않습니다.", userIds)); + } } @Transactional(readOnly = true) public User findById(final Long userId) { - return userRepository.findById(userId); + return userRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(String.format("[%d] 식별번호에 해당하는 회원이 존재하지 않습니다.", userId))); + } + + @Transactional(readOnly = true) + public List findAllUserInTeam(final Long teamId) { + Team team = teamService.findById(teamId); + + return team.getUsers().stream() + .map(SelectUserResponse::new) + .collect(Collectors.toList()); } } diff --git a/src/main/java/playground/controller/user/request/CreateUserRequest.java b/src/main/java/playground/service/user/request/CreateUserRequest.java similarity index 52% rename from src/main/java/playground/controller/user/request/CreateUserRequest.java rename to src/main/java/playground/service/user/request/CreateUserRequest.java index ed794eb..044984c 100644 --- a/src/main/java/playground/controller/user/request/CreateUserRequest.java +++ b/src/main/java/playground/service/user/request/CreateUserRequest.java @@ -1,12 +1,15 @@ -package playground.controller.user.request; +package playground.service.user.request; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import playground.domain.team.Team; import playground.domain.user.User; +import playground.domain.user.vo.JobPosition; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -22,13 +25,22 @@ public class CreateUserRequest { @NotBlank private String name; - public CreateUserRequest(final String email, final String password, final String name) { + @NotNull + private Long teamId; + + @NotBlank + private String jobPosition; + + public CreateUserRequest(final String email, final String password, final String name, + final Long teamId, final String jobPosition) { this.email = email; this.password = password; this.name = name; + this.teamId = teamId; + this.jobPosition = jobPosition; } - public User toUser() { - return new User(email, password, name); + public User toUser(final Team team) { + return new User(email, password, name, team, JobPosition.valueOf(jobPosition)); } } diff --git a/src/main/java/playground/service/user/response/SelectUserResponse.java b/src/main/java/playground/service/user/response/SelectUserResponse.java new file mode 100644 index 0000000..3cc2c2c --- /dev/null +++ b/src/main/java/playground/service/user/response/SelectUserResponse.java @@ -0,0 +1,26 @@ +package playground.service.user.response; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.user.User; +import playground.domain.user.vo.JobPosition; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SelectUserResponse { + + private Long id; + private JobPosition jobPosition; + private String teamName; + private String name; + private String jobPositionText; + + public SelectUserResponse(final User user) { + this.id = user.getId(); + this.jobPosition = user.getJobPosition(); + this.teamName = user.getTeam().getName(); + this.name = user.getName(); + this.jobPositionText = user.getJobPosition().getText(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 067a3e0..100fa7c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,3 +7,14 @@ spring: h2: console: enabled: true + jpa: + open-in-view: false + hibernate: + ddl-auto: create + properties: + hibernate: + default_batch_fetch_size: 100 + format_sql: true + show_sql: true + defer-datasource-initialization: true + diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 7713d51..5f6e7c8 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,39 +1,17 @@ -drop table if exists user; -create table user -( - id bigint not null auto_increment primary key, - email varchar(255), - password varchar(255), - name varchar(255), - constraint user_email_constraint unique (email) -); +insert into team(team_id, name) +values (1, '운영팀'); -drop table if exists document; -create table document -( - id bigint not null auto_increment primary key, - title varchar(255) not null, - category varchar(255) not null, - contents varchar(255) not null, - drafter_id bigint not null, - approval_state varchar(255) not null, - foreign key (drafter_id) references user (id) -); +insert into team(team_id, name) +values (2, 'CS팀'); -drop table if exists documentApproval; -create table document_approval -( - approver_id bigint not null, - document_id bigint not null, - approval_state varchar(255) not null, - approval_order tinyint not null, - approval_comment varchar(255), - foreign key (approver_id) references user (id), - foreign key (document_id) references document (id) -); +insert into user(user_id, email, password, name, team_id, job_position) +values (1, 'wbluke@gmail.com', '1234', '박우빈1', 1, 'TEAM_MEMBER'); -insert into user(id, email, password, name) -values (1, 'wbluke@gmail.com', '1234', '박우빈'); +insert into user(user_id, email, password, name, team_id, job_position) +values (2, 'wbluke2@gmail.com', '1234', '박우빈2', 2, 'TEAM_MEMBER'); -insert into user(id, email, password, name) -values (2, 'wbluke2@gmail.com', '1234', '닉우빈'); +insert into user(user_id, email, password, name, team_id, job_position) +values (3, 'wbluke3@gmail.com', '1234', '박우빈3', 1, 'TEAM_MEMBER'); + +insert into user(user_id, email, password, name, team_id, job_position) +values (4, 'wbluke4@gmail.com', '1234', '박우빈4', 2, 'TEAM_MEMBER'); diff --git a/src/test/java/playground/QueryTest.java b/src/test/java/playground/QueryTest.java new file mode 100644 index 0000000..1bbe4e2 --- /dev/null +++ b/src/test/java/playground/QueryTest.java @@ -0,0 +1,34 @@ +package playground; + +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.transaction.annotation.Transactional; +import playground.repository.document.DocumentApprovalRepository; +import playground.repository.document.DocumentRepository; +import playground.service.user.UserService; + +@SpringBootTest +@Transactional +public class QueryTest { + + @Autowired + private DocumentApprovalRepository documentApprovalRepository; + + @Autowired + private DocumentRepository documentRepository; + + @Autowired + private UserService userService; + + @Test + @DisplayName("") + void test() { + //given + + //when + + //then + } +} diff --git a/src/test/java/playground/controller/document/DocumentControllerTest.java b/src/test/java/playground/controller/document/DocumentControllerTest.java index 50c2bf9..5651371 100644 --- a/src/test/java/playground/controller/document/DocumentControllerTest.java +++ b/src/test/java/playground/controller/document/DocumentControllerTest.java @@ -7,8 +7,8 @@ import org.mockito.Mock; import org.springframework.http.MediaType; import playground.common.AbstractControllerTest; -import playground.controller.document.request.CreateDocumentRequest; import playground.service.document.DocumentService; +import playground.service.document.request.CreateDocumentRequest; import java.util.Collections; import java.util.List; @@ -111,7 +111,7 @@ void create_fail_empty_approver_ids(List invalidApproverIds) throws Except @Test @DisplayName("문서를 조회한다.") - void select() throws Exception { + void find() throws Exception { mockMvc.perform(get("/api/documents/{documentId}", 1L) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); @@ -119,10 +119,18 @@ void select() throws Exception { @Test @DisplayName("Outbox 문서 리스트를 조회한다.") - void selectOutBox() throws Exception { + void findOutBox() throws Exception { mockMvc.perform(get("/api/documents/outbox") .param("drafterId", "1") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); } + + @Test + @DisplayName("모든 문서 항목을 조회한다.") + void findCategories() throws Exception { + mockMvc.perform(get("/api/documents/categories") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } } diff --git a/src/test/java/playground/controller/team/TeamControllerTest.java b/src/test/java/playground/controller/team/TeamControllerTest.java new file mode 100644 index 0000000..178c400 --- /dev/null +++ b/src/test/java/playground/controller/team/TeamControllerTest.java @@ -0,0 +1,66 @@ +package playground.controller.team; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import playground.common.AbstractControllerTest; +import playground.service.team.TeamService; +import playground.service.team.request.CreateTeamRequest; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +class TeamControllerTest extends AbstractControllerTest { + + @Mock + private TeamService teamService; + + @InjectMocks + private TeamController teamController; + + @Override + protected Object setController() { + return teamController; + } + + @Test + @DisplayName("팀 생성 요청을 받아 팀을 생성 후, HTTP 201을 반환한다.") + void create() throws Exception { + CreateTeamRequest createTeamRequest = new CreateTeamRequest("정산시스템팀"); + + mockMvc.perform(post("/api/teams") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createTeamRequest))) + .andExpect(status().isCreated()); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("팀 이름이 공백 또는 null있을 경우, 예외가 발생한다.") + void create_fail_empty_name(String invalidName) throws Exception { + CreateTeamRequest createTeamRequest = new CreateTeamRequest(invalidName); + + mockMvc.perform(post("/api/teams") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createTeamRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("모든 팀을 조회한다.") + void findAll() throws Exception { + mockMvc.perform(get("/api/teams") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/playground/controller/user/UserControllerTest.java b/src/test/java/playground/controller/user/UserControllerTest.java index f0657d9..1e02dee 100644 --- a/src/test/java/playground/controller/user/UserControllerTest.java +++ b/src/test/java/playground/controller/user/UserControllerTest.java @@ -4,15 +4,26 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.springframework.http.MediaType; import playground.common.AbstractControllerTest; -import playground.controller.user.request.CreateUserRequest; +import playground.domain.team.Team; +import playground.domain.user.User; import playground.service.user.UserService; +import playground.service.user.request.CreateUserRequest; +import playground.service.user.response.SelectUserResponse; +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static playground.domain.user.vo.JobPosition.TEAM_MEMBER; public class UserControllerTest extends AbstractControllerTest { @@ -27,9 +38,9 @@ protected Object setController() { @Test @DisplayName("사용자 생성 요청을 받아 사용자를 생성 후, HTTP 201을 반환한다.") void create() throws Exception { - CreateUserRequest createUserRequest = new CreateUserRequest("seongbeen93@naver.com", "password", "김성빈"); + CreateUserRequest createUserRequest = new CreateUserRequest("seongbeen93@naver.com", "password", "김성빈", 1L, TEAM_MEMBER.getText()); - mockMvc.perform(post("/api/user") + mockMvc.perform(post("/api/users") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(createUserRequest))) @@ -40,9 +51,9 @@ void create() throws Exception { @ValueSource(strings = {"idnaver.com", "", "@naver.com"}) @DisplayName("이메일 형식이 올바르지 않을 경우, 예외가 발생한다.") void create_fail_invalid_email(String invalidEmail) throws Exception { - CreateUserRequest createUserRequest = new CreateUserRequest(invalidEmail, "password", "김성빈"); + CreateUserRequest createUserRequest = new CreateUserRequest(invalidEmail, "password", "김성빈", 1L, TEAM_MEMBER.getText()); - mockMvc.perform(post("/api/user") + mockMvc.perform(post("/api/users") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(createUserRequest))) @@ -53,9 +64,9 @@ void create_fail_invalid_email(String invalidEmail) throws Exception { @NullAndEmptySource @DisplayName("비밀번호가 공백 또는 null있을 경우, 예외가 발생한다.") void create_fail_empty_password(String invalidPassword) throws Exception { - CreateUserRequest createUserRequest = new CreateUserRequest("seongbeen93@naver.com", invalidPassword, "김성빈"); + CreateUserRequest createUserRequest = new CreateUserRequest("seongbeen93@naver.com", invalidPassword, "김성빈", 1L, TEAM_MEMBER.getText()); - mockMvc.perform(post("/api/user") + mockMvc.perform(post("/api/users") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(createUserRequest))) @@ -66,12 +77,44 @@ void create_fail_empty_password(String invalidPassword) throws Exception { @NullAndEmptySource @DisplayName("이름이 공백 또는 null일 경우, 예외가 발생한다.") void create_fail_empty_name(String invalidName) throws Exception { - CreateUserRequest createUserRequest = new CreateUserRequest("seongbeen93@naver.com", "password", invalidName); + CreateUserRequest createUserRequest = new CreateUserRequest("seongbeen93@naver.com", "password", invalidName, 1L, TEAM_MEMBER.getText()); - mockMvc.perform(post("/api/user") + mockMvc.perform(post("/api/users") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(createUserRequest))) .andExpect(status().isBadRequest()); } + + @ParameterizedTest + @NullSource + @DisplayName("팀 식별번호가 null일 경우, 예외가 발생한다.") + void create_fail_empty_team(Long invalidTeamId) throws Exception { + CreateUserRequest createUserRequest = new CreateUserRequest("seongbeen93@naver.com", "password", "김성빈", invalidTeamId, TEAM_MEMBER.getText()); + + mockMvc.perform(post("/api/users") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createUserRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("특정 팀에 속한 사용자 조회 요청을 전달받아, 사용자들의 정보를 반환한다.") + void findAllUserInTeam() throws Exception { + User user = mock(User.class); + Team team = mock(Team.class); + given(user.getId()).willReturn(1L); + given(user.getName()).willReturn("김성빈"); + given(user.getJobPosition()).willReturn(TEAM_MEMBER); + given(user.getTeam()).willReturn(team); + given(team.getName()).willReturn("운영팀"); + SelectUserResponse selectUserResponse = new SelectUserResponse(user); + given(userService.findAllUserInTeam(anyLong())).willReturn(Collections.singletonList(selectUserResponse)); + + mockMvc.perform(get("/api/users") + .param("teamId", "1") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } } diff --git a/src/test/java/playground/domain/document/DocumentApprovalTest.java b/src/test/java/playground/domain/document/DocumentApprovalTest.java index 9fac8e1..0da3893 100644 --- a/src/test/java/playground/domain/document/DocumentApprovalTest.java +++ b/src/test/java/playground/domain/document/DocumentApprovalTest.java @@ -2,8 +2,14 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import playground.domain.document.vo.ApprovalState; +import playground.domain.document.vo.Category; +import playground.domain.team.Team; +import playground.domain.user.User; +import playground.domain.user.vo.JobPosition; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; class DocumentApprovalTest { @@ -11,16 +17,37 @@ class DocumentApprovalTest { @DisplayName("결재 정보를 저장한다.") void create() { //given - long approverId = 1L; - long documentId = 1L; + Team team = mock(Team.class); + User drafter = User.builder() + .email("test1@naver.com") + .password("Password123!") + .name("drafter") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + + User approver = User.builder() + .email("test2@naver.com") + .password("Password123!") + .name("approver") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + + Document document = Document.builder() + .drafter(drafter) + .category(Category.EDUCATION) + .title("교육비 정산") + .contents("교육비 정산 결재") + .build(); int approvalOrder = 1; //when - DocumentApproval documentApproval = DocumentApproval.of(approverId, documentId, approvalOrder); + DocumentApproval documentApproval = DocumentApproval.of(approver, document, approvalOrder); //then assertThat(documentApproval) - .extracting("approverId", "documentId", "approvalState", "approvalOrder", "approvalComment") - .containsExactly(approverId, documentId, ApprovalState.DRAFTING, approvalOrder, null); + .extracting("approver", "document", "approvalState", "approvalOrder", "approvalComment") + .containsExactly(approver, document, ApprovalState.DRAFTING, approvalOrder, null); } } diff --git a/src/test/java/playground/domain/document/DocumentApprovalsTest.java b/src/test/java/playground/domain/document/DocumentApprovalsTest.java new file mode 100644 index 0000000..bc9257c --- /dev/null +++ b/src/test/java/playground/domain/document/DocumentApprovalsTest.java @@ -0,0 +1,113 @@ +package playground.domain.document; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import playground.domain.document.vo.Category; +import playground.domain.team.Team; +import playground.domain.user.User; +import playground.domain.user.vo.JobPosition; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.Mockito.mock; + +class DocumentApprovalsTest { + + @Test + @DisplayName("결재자를 등록한다.") + void enroll() { + //given + Team team = mock(Team.class); + User drafter = User.builder() + .email("test1@naver.com") + .password("Password123!") + .name("drafter") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + + User approver1 = User.builder() + .email("test2@naver.com") + .password("Password123!") + .name("approver1") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + + User approver2 = User.builder() + .email("test2@naver.com") + .password("Password123!") + .name("approver2") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + + Document document = Document.builder() + .drafter(drafter) + .category(Category.EDUCATION) + .title("교육비 정산") + .contents("교육비 정산 결재") + .build(); + + DocumentApprovals documentApprovals = new DocumentApprovals(); + + //when + documentApprovals.enroll(Arrays.asList(approver1, approver2), document); + + //then + assertThat(documentApprovals.getDocumentApprovals()).hasSize(2); + } + + @Test + @DisplayName("이미 결재자가 등록된 상태에서, 결재자를 추가할 경우 예외가 발생한다.") + void enroll_fail() { + //given + Team team = mock(Team.class); + User drafter = User.builder() + .email("test1@naver.com") + .password("Password123!") + .name("drafter") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + User approver1 = User.builder() + .email("test2@naver.com") + .password("Password123!") + .name("approver1") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + User approver2 = User.builder() + .email("test2@naver.com") + .password("Password123!") + .name("approver2") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + + Document document = Document.builder() + .drafter(drafter) + .category(Category.EDUCATION) + .title("교육비 정산") + .contents("교육비 정산 결재") + .build(); + + DocumentApprovals documentApprovals = new DocumentApprovals(); + documentApprovals.enroll(Arrays.asList(approver1, approver2), document); + User approver3 = User.builder() + .email("test4@naver.com") + .password("Password123!") + .name("approver3") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + + //when, then + assertThatIllegalStateException() + .isThrownBy(() -> documentApprovals.enroll(Collections.singletonList(approver3), document)) + .withMessage("결재자 추가 등록이 불가능합니다."); + } +} diff --git a/src/test/java/playground/domain/document/DocumentTest.java b/src/test/java/playground/domain/document/DocumentTest.java index 3925027..5c34154 100644 --- a/src/test/java/playground/domain/document/DocumentTest.java +++ b/src/test/java/playground/domain/document/DocumentTest.java @@ -2,8 +2,14 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import playground.domain.document.vo.ApprovalState; +import playground.domain.document.vo.Category; +import playground.domain.team.Team; +import playground.domain.user.User; +import playground.domain.user.vo.JobPosition; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; class DocumentTest { @@ -12,21 +18,28 @@ class DocumentTest { void create() { //given String title = "교육비 결재"; - String category = "EDUCATION"; + Category category = Category.EDUCATION; String contents = "교육비"; - long drafterId = 1L; + Team team = mock(Team.class); + User drafter = User.builder() + .email("test@naver.com") + .password("Password123!") + .name("drafter") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); //when Document document = Document.builder() .title(title) .category(category) .contents(contents) - .drafterId(drafterId) + .drafter(drafter) .build(); //then assertThat(document) - .extracting("title", "category", "contents", "drafterId", "approvalState") - .containsExactly(title, Category.valueOf(category), contents, drafterId, ApprovalState.DRAFTING); + .extracting("title", "category", "contents", "drafter", "approvalState") + .containsExactly(title, category, contents, drafter, ApprovalState.DRAFTING); } } diff --git a/src/test/java/playground/domain/team/TeamTest.java b/src/test/java/playground/domain/team/TeamTest.java new file mode 100644 index 0000000..02e0309 --- /dev/null +++ b/src/test/java/playground/domain/team/TeamTest.java @@ -0,0 +1,24 @@ +package playground.domain.team; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TeamTest { + + @Test + @DisplayName("팀을 생성한다.") + void create() { + //given + String name = "정산시스템팀"; + + //when + Team team = new Team(name); + + //then + assertThat(team) + .extracting("name") + .isEqualTo(name); + } +} diff --git a/src/test/java/playground/domain/user/UserTest.java b/src/test/java/playground/domain/user/UserTest.java index 95ad833..748ebf8 100644 --- a/src/test/java/playground/domain/user/UserTest.java +++ b/src/test/java/playground/domain/user/UserTest.java @@ -2,8 +2,11 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import playground.domain.team.Team; +import playground.domain.user.vo.JobPosition; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; class UserTest { @@ -11,16 +14,23 @@ class UserTest { @DisplayName("사용자를 생성한다.") void create() { //given + Team team = mock(Team.class); String email = "seongbeen93@naver.com"; String password = "password"; String name = "김성빈"; //when - User user = new User(email, password, name); + User user = User.builder() + .email(email) + .password(password) + .name(name) + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); //then assertThat(user) - .extracting("email", "password", "name") - .containsExactly(email, password, name); + .extracting("email", "password", "name", "team") + .containsExactly(email, password, name, team); } } diff --git a/src/test/java/playground/repository/document/DocumentApprovalRepositoryTest.java b/src/test/java/playground/repository/document/DocumentApprovalRepositoryTest.java deleted file mode 100644 index 2347be3..0000000 --- a/src/test/java/playground/repository/document/DocumentApprovalRepositoryTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package playground.repository.document; - -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.transaction.annotation.Transactional; -import playground.domain.document.Document; -import playground.domain.document.DocumentApproval; -import playground.domain.user.User; -import playground.repository.user.UserRepository; - -import java.util.ArrayList; - -import static org.assertj.core.api.Assertions.assertThat; - -@SpringBootTest -@Transactional -class DocumentApprovalRepositoryTest { - - @Autowired - private DocumentApprovalRepository documentApprovalRepository; - - @Autowired - private UserRepository userRepository; - - @Autowired - private DocumentRepository documentRepository; - - @Test - @DisplayName("결재 정보를 저장한다.") - void saveAll() { - //given - User drafter = new User("drafter@naver.com", "Password123!", "기안자"); - User approver1 = new User("approver1@naver.com", "Password123!", "김성빈1"); - User approver2 = new User("approver2@naver.com", "Password123!", "김성빈2"); - Long drafterId = userRepository.save(drafter); - Long approverId1 = userRepository.save(approver1); - Long approverId2 = userRepository.save(approver2); - - Document document = Document.builder() - .title("교육비 결재") - .drafterId(drafterId) - .category("EDUCATION") - .contents("교육비") - .build(); - Long documentId = documentRepository.save(document); - - ArrayList documentApprovals = new ArrayList<>(); - documentApprovals.add(DocumentApproval.of(approverId1, documentId, 1)); - documentApprovals.add(DocumentApproval.of(approverId2, documentId, 2)); - - //when - documentApprovalRepository.saveAll(documentApprovals); - - //then - assertThat(documentApprovalRepository.findAll()).hasSize(2); - } -} diff --git a/src/test/java/playground/repository/document/DocumentRepositoryTest.java b/src/test/java/playground/repository/document/DocumentRepositoryTest.java index 0bf50c7..3751ca0 100644 --- a/src/test/java/playground/repository/document/DocumentRepositoryTest.java +++ b/src/test/java/playground/repository/document/DocumentRepositoryTest.java @@ -1,104 +1,157 @@ package playground.repository.document; +import org.junit.jupiter.api.BeforeEach; 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.transaction.annotation.Transactional; -import playground.domain.document.ApprovalState; -import playground.domain.document.Category; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import playground.domain.document.Document; - +import playground.domain.document.vo.ApprovalState; +import playground.domain.document.vo.Category; +import playground.domain.team.Team; +import playground.domain.user.User; +import playground.domain.user.vo.JobPosition; +import playground.service.document.response.SelectCategoryResponse; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceUnitUtil; +import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -@SpringBootTest -@Transactional +@DataJpaTest class DocumentRepositoryTest { @Autowired private DocumentRepository documentRepository; + @Autowired + private EntityManager entityManager; + + @Autowired + private EntityManagerFactory entityManagerFactory; + + private PersistenceUnitUtil persistenceUnitUtil; + + @BeforeEach + void setUp() { + persistenceUnitUtil = entityManagerFactory.getPersistenceUnitUtil(); + } + @Test - @DisplayName("문서를 저장한다.") - void save() { + @DisplayName("기안자의 정보, 결재 정보 리스트가 포함된 문서를 조회한다.") + void findDocumentAndDrafterAndDocumentApprovalsById() { //given + Team team = new Team("정산시스템팀"); + entityManager.persist(team); + User drafter = User.builder() + .email("test@naver.com") + .password("Password123!") + .name("drafter") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + entityManager.persist(drafter); Document document = Document.builder() - .title("교육비 결재") - .category("EDUCATION") - .contents("교육비") - .drafterId(1L) + .drafter(drafter) + .category(Category.EDUCATION) + .title("교육비 정산") + .contents("교육비 정산 결재") .build(); + document.enrollApprovals(Collections.singletonList(drafter)); + documentRepository.save(document); + entityManager.flush(); + entityManager.clear(); //when - long documentId = documentRepository.save(document); + Optional fetchedDocument = documentRepository.findDocumentAndDrafterAndDocumentApprovalsById(document.getId()); //then - assertThat(documentId).isNotZero(); + assertThat(fetchedDocument).isNotEmpty(); + assertThat(persistenceUnitUtil.isLoaded(fetchedDocument.get().getDrafter())).isTrue(); + assertThat(persistenceUnitUtil.isLoaded(fetchedDocument.get().getDocumentApprovals())).isTrue(); } @Test - @DisplayName("문서를 조회한다.") - void findById() { + @DisplayName("기안자의 정보가 포함된 조건에 맞는 모든 문서를 조회한다.") + void findAllDocumentAndDrafterByDrafterIdAndApprovalState() { //given - String title = "교육비 결재"; - String category = "EDUCATION"; - String contents = "교육비"; - long drafterId = 1L; - + Team team = new Team("정산시스템팀"); + entityManager.persist(team); + User drafter = User.builder() + .email("test@naver.com") + .password("Password123!") + .name("drafter") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) + .build(); + entityManager.persist(drafter); Document document = Document.builder() - .title(title) - .category(category) - .contents(contents) - .drafterId(drafterId) + .drafter(drafter) + .category(Category.EDUCATION) + .title("교육비 정산") + .contents("교육비 정산 결재") .build(); - long documentId = documentRepository.save(document); + documentRepository.save(document); + entityManager.flush(); + entityManager.clear(); //when - Document fetchedDocument = documentRepository.findById(documentId); + List documents = documentRepository.findAllDocumentAndDrafterByDrafterIdAndApprovalState(drafter.getId(), ApprovalState.DRAFTING); //then - assertThat(fetchedDocument) - .extracting("title", "category", "contents", "drafterId", "approvalState") - .containsExactly(title, Category.valueOf(category), contents, drafterId, ApprovalState.DRAFTING); - } - - @Test - @DisplayName("식별번호에 일치하는 문서가 존재하지 않을 경우, 예외가 발생한다.") - void findById_fail_empty_result() { - //when, then - assertThatIllegalArgumentException() - .isThrownBy(() -> documentRepository.findById(0L)) - .withMessageContaining("해당하는 문서가 존재하지 않습니다."); + assertThat(documents).hasSize(1); + assertThat(persistenceUnitUtil.isLoaded(documents.get(0).getDrafter())).isTrue(); } @Test - @DisplayName("기안자 식별번호화 결재 상태가 일치하는 문서들을 조회한다.") - void findAllByDrafterIdAndApprovalState() { + @DisplayName("중복 제거된 문서 항목을 반환한다.") + void findCategories() { //given - long drafterId = 1L; - Document document1 = Document.builder() - .title("교육비 결재") - .category("EDUCATION") - .contents("교육비") - .drafterId(drafterId) + Team team = new Team("정산시스템팀"); + entityManager.persist(team); + + User drafter = User.builder() + .email("test@naver.com") + .password("Password123!") + .name("drafter") + .team(team) + .jobPosition(JobPosition.TEAM_MEMBER) .build(); + entityManager.persist(drafter); + Document document = Document.builder() + .drafter(drafter) + .category(Category.EDUCATION) + .title("교육비 정산") + .contents("교육비 정산 결재") + .build(); Document document2 = Document.builder() - .title("운영비 결재") - .category("OPERATING_EXPENSES") - .contents("운영비") - .drafterId(drafterId) + .drafter(drafter) + .category(Category.EDUCATION) + .title("교육비 정산") + .contents("교육비 정산 결재") + .build(); + Document document3 = Document.builder() + .drafter(drafter) + .category(Category.OPERATING_EXPENSES) + .title("교육비 정산") + .contents("교육비 정산 결재") .build(); - documentRepository.save(document1); + documentRepository.save(document); documentRepository.save(document2); + documentRepository.save(document3); //when - List documents = documentRepository.findAllByDrafterIdAndApprovalState(drafterId, ApprovalState.DRAFTING); + List selectCategoryResponses = documentRepository.findCategories(); //then - assertThat(documents).hasSize(2); + assertThat(selectCategoryResponses).hasSize(2); + assertThat(selectCategoryResponses) + .extracting("category") + .containsExactly(Category.EDUCATION, Category.OPERATING_EXPENSES); } } diff --git a/src/test/java/playground/repository/user/UserRepositoryTest.java b/src/test/java/playground/repository/user/UserRepositoryTest.java deleted file mode 100644 index 98690c4..0000000 --- a/src/test/java/playground/repository/user/UserRepositoryTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package playground.repository.user; - -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.DuplicateKeyException; -import org.springframework.transaction.annotation.Transactional; -import playground.domain.user.User; - -import java.util.Arrays; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -@SpringBootTest -@Transactional -class UserRepositoryTest { - - @Autowired - private UserRepository userRepository; - - @Test - @DisplayName("회원을 저장한다.") - void save() { - //given - User user = new User("a@naver.com", "password", "김성빈"); - - //when - long userId = userRepository.save(user); - - //then - assertThat(userId).isNotZero(); - } - - @Test - @DisplayName("중복된 이메일일 경우, 예외가 발생한다.") - void save_fail_duplicated_email() { - //given - User user1 = new User("a@naver.com", "password", "김성빈"); - userRepository.save(user1); - User user2 = new User("a@naver.com", "password2", "김성빈2"); - - //when, then - assertThatThrownBy(() -> userRepository.save(user2)) - .isInstanceOf(DuplicateKeyException.class); - } - - @Test - @DisplayName("식별번호에 일치하는 모든 사용자를 조회한다.") - void findAllById() { - //given - User user1 = new User("a1@naver.com", "password", "김성빈1"); - User user2 = new User("a2@naver.com", "password2", "김성빈2"); - Long userId1 = userRepository.save(user1); - Long userId2 = userRepository.save(user2); - - //when - List users = userRepository.findAllById(Arrays.asList(userId1, userId2)); - - //then - assertThat(users).hasSize(2); - } - - @Test - @DisplayName("식별번호에 일치하는 사용자를 조회한다.") - void findById() { - //given - String email = "a1@naver.com"; - String password = "password"; - String name = "김성빈1"; - User user = new User(email, password, name); - Long userId = userRepository.save(user); - - //when - User fetchedUser = userRepository.findById(userId); - - //then - assertThat(fetchedUser) - .extracting("email", "password", "name") - .containsExactly(email, password, name); - } - - @Test - @DisplayName("식별번호에 일치하는 사용자가 존재하지 않을 경우, 예외가 발생한다.") - void findById_fail_empty_result() { - //when, then - assertThatIllegalArgumentException() - .isThrownBy(() -> userRepository.findById(0L)) - .withMessageContaining("해당하는 회원이 존재하지 않습니다."); - } -} diff --git a/src/test/java/playground/service/document/DocumentServiceTest.java b/src/test/java/playground/service/document/DocumentServiceTest.java index 508ba88..31d8c6d 100644 --- a/src/test/java/playground/service/document/DocumentServiceTest.java +++ b/src/test/java/playground/service/document/DocumentServiceTest.java @@ -6,22 +6,29 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import playground.controller.document.request.CreateDocumentRequest; -import playground.domain.document.ApprovalState; import playground.domain.document.Document; +import playground.domain.document.DocumentApproval; +import playground.domain.document.vo.ApprovalState; +import playground.domain.document.vo.Category; +import playground.domain.team.Team; import playground.domain.user.User; +import playground.domain.user.vo.JobPosition; import playground.repository.document.DocumentApprovalRepository; import playground.repository.document.DocumentRepository; +import playground.service.document.request.CreateDocumentRequest; import playground.service.document.response.SelectDocumentResponse; import playground.service.document.response.SelectSingleOutBoxResponse; import playground.service.user.UserService; +import javax.persistence.EntityNotFoundException; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; @@ -47,7 +54,7 @@ class DocumentServiceTest { @Test @DisplayName("문서를 저장한다.") - void save() { + void create() { //given User user = mock(User.class); CreateDocumentRequest createDocumentRequest = new CreateDocumentRequest("교육비 결재 요청", "EDUCATION", @@ -55,46 +62,55 @@ void save() { given(userService.findAllById(anyList())).willReturn(Arrays.asList(user, user)); //when - documentService.save(createDocumentRequest); + documentService.create(createDocumentRequest); //then verify(userService, times(1)).findAllById(anyList()); verify(documentRepository, times(1)).save(any(Document.class)); - verify(documentApprovalRepository, times(1)).saveAll(anyList()); } @Test - @DisplayName("기안자 또는 결재자의 정보가 존재하지 않을 경우, 예외가 발생한다.") - void save_fail_not_exist_drafter_or_approver() { + @DisplayName("결재자가 한 명이라도 존재하지 않을 경우, 예외가 발생한다.") + void create_fail_not_exist_approver() { //given CreateDocumentRequest createDocumentRequest = new CreateDocumentRequest("교육비 결재 요청", "EDUCATION", "교육비", 1L, Collections.singletonList(1L)); - String errorMessage = "전달받은 회원 식별자와 일치하는 회원을 모두 찾지 못했습니다."; + String errorMessage = "식별번호와 일치하는 결재자를 모두 찾지 못했습니다."; //when, then - assertThatIllegalArgumentException() - .isThrownBy(() -> documentService.save(createDocumentRequest)) - .withMessage(errorMessage); + assertThatThrownBy(() -> documentService.create(createDocumentRequest)) + .isInstanceOf(EntityNotFoundException.class) + .hasMessageContaining(errorMessage); } @Test @DisplayName("문서를 조회한다.") - void select() { + void find() { //given - Document document = Document.builderForDao() - .id(1L) - .title("교육비 결재") - .category("EDUCATION") - .contents("교육비") - .drafterId(1L) - .approvalState("DRAFTING") - .build(); - User user = new User("a@naver.com", "Password123!", "김성빈"); - given(documentRepository.findById(anyLong())).willReturn(document); - given(userService.findById(anyLong())).willReturn(user); + User drafter = mock(User.class); + given(drafter.getId()).willReturn(1L); + given(drafter.getName()).willReturn("기안자"); + given(drafter.getJobPosition()).willReturn(JobPosition.PART_MANAGER); + + Team team = mock(Team.class); + given(drafter.getTeam()).willReturn(team); + + Document document = mock(Document.class); + given(document.getId()).willReturn(1L); + given(document.getDrafter()).willReturn(drafter); + given(document.getTitle()).willReturn("교육비 결재"); + given(document.getCategory()).willReturn(Category.EDUCATION); + given(document.getContents()).willReturn("교육비"); + given(document.getApprovalState()).willReturn(ApprovalState.DRAFTING); + + DocumentApproval documentApproval = mock(DocumentApproval.class); + given(documentApproval.getApprover()).willReturn(drafter); + given(documentApproval.getApprovalState()).willReturn(ApprovalState.DRAFTING); + given(documentApprovalRepository.findAllDocumentApprovalAndApproverAndTeamByIds(anyList())).willReturn(Collections.singletonList(documentApproval)); + given(documentRepository.findDocumentAndDrafterAndDocumentApprovalsById(anyLong())).willReturn(Optional.of(document)); //when - SelectDocumentResponse selectDocumentResponse = documentService.select(1L); + SelectDocumentResponse selectDocumentResponse = documentService.find(1L); //then assertThat(selectDocumentResponse).isNotNull(); @@ -102,50 +118,28 @@ void select() { @Test @DisplayName("문서가 존재하지 않을 시, 예외가 발생한다.") - void select_fail_not_found_document() { - //given - String errorMessage = "해당하는 문서가 존재하지 않습니다."; - given(documentRepository.findById(anyLong())).willThrow(new IllegalArgumentException(errorMessage)); - + void find_fail_not_found_document() { //when, then - assertThatIllegalArgumentException() - .isThrownBy(() -> documentService.select(1L)) - .withMessageContaining(errorMessage); + assertThatThrownBy(() -> documentService.find(1L)) + .isInstanceOf(EntityNotFoundException.class) + .hasMessageContaining("해당하는 문서가 존재하지 않습니다."); } @Test - @DisplayName("기안자가 존재하지 않을 시, 예외가 발생한다.") - void select_fail_not_found_user() { + @DisplayName("Outbox 문서 리스트를 조회한다.") + void findOutBox() { //given Document document = mock(Document.class); - given(documentRepository.findById(anyLong())).willReturn(document); - String errorMessage = "해당하는 회원이 존재하지 않습니다."; - given(userService.findById(anyLong())).willThrow(new IllegalArgumentException(errorMessage)); + given(document.getId()).willReturn(1L); + given(document.getTitle()).willReturn("교육비 결재"); + given(document.getCategory()).willReturn(Category.EDUCATION); + given(document.getApprovalState()).willReturn(ApprovalState.DRAFTING); - //when, then - assertThatIllegalArgumentException() - .isThrownBy(() -> documentService.select(1L)) - .withMessageContaining(errorMessage); - } - - @Test - @DisplayName("Outbox 문서 리스트를 조회한다.") - void selectOutBox() { - //given - long drafterId = 1L; - Document document = Document.builderForDao() - .id(1L) - .title("교육비 결재") - .category("EDUCATION") - .contents("교육비") - .drafterId(drafterId) - .approvalState("DRAFTING") - .build(); - given(documentRepository.findAllByDrafterIdAndApprovalState(anyLong(), any(ApprovalState.class))) + given(documentRepository.findAllDocumentAndDrafterByDrafterIdAndApprovalState(anyLong(), any(ApprovalState.class))) .willReturn(Collections.singletonList(document)); //when - List selectMultiOutboxResponse = documentService.selectOutBox(drafterId); + List selectMultiOutboxResponse = documentService.findOutBox(1L); //then assertThat(selectMultiOutboxResponse).hasSize(1); @@ -153,15 +147,15 @@ void selectOutBox() { @Test @DisplayName("Outbox 문서가 없다면, 예외가 발생한다.") - void selectOutBox_fail_empty_result() { + void findOutBox_fail_empty_result() { //given String errorMessage = "현재 결재중인 문서가 존재하지 않습니다."; - given(documentRepository.findAllByDrafterIdAndApprovalState(anyLong(), any(ApprovalState.class))) + given(documentRepository.findAllDocumentAndDrafterByDrafterIdAndApprovalState(anyLong(), any(ApprovalState.class))) .willReturn(Collections.emptyList()); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> documentService.selectOutBox(1L)) + .isThrownBy(() -> documentService.findOutBox(1L)) .withMessageContaining(errorMessage); } } diff --git a/src/test/java/playground/service/team/TeamServiceTest.java b/src/test/java/playground/service/team/TeamServiceTest.java new file mode 100644 index 0000000..60e94fb --- /dev/null +++ b/src/test/java/playground/service/team/TeamServiceTest.java @@ -0,0 +1,119 @@ +package playground.service.team; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.dao.DuplicateKeyException; +import playground.domain.team.Team; +import playground.repository.team.TeamRepository; +import playground.service.team.request.CreateTeamRequest; +import playground.service.team.response.SelectTeamResponse; + +import javax.persistence.EntityNotFoundException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class TeamServiceTest { + + @Mock + private TeamRepository teamRepository; + + @InjectMocks + private TeamService teamService; + + @Test + @DisplayName("팀을 저장한다.") + void create() { + //given + CreateTeamRequest createTeamRequest = new CreateTeamRequest("정산시스템팀"); + + //when + teamService.create(createTeamRequest); + + //then + verify(teamRepository, times(1)).save(any(Team.class)); + } + + @Test + @DisplayName("중복된 팀 명으로 저장할경우, 예외가 발생한다.") + void create_fail_duplicated_name() { + //given + CreateTeamRequest createTeamRequest = new CreateTeamRequest("정산시스템팀"); + given(teamRepository.save(any(Team.class))).willThrow(new DuplicateKeyException("이름 중복")); + + //when, then + assertThatIllegalArgumentException() + .isThrownBy(() -> teamService.create(createTeamRequest)) + .withMessageContaining("이미 존재하는 팀입니다."); + } + + @Test + @DisplayName("모든 팀을 조회한다.") + void findAll() { + //given + Team team1 = new Team("정산시스템팀"); + Team team2 = new Team("서비스개발팀"); + List teams = Arrays.asList(team1, team2); + given(teamRepository.findAll()).willReturn(teams); + + //when + List selectTeamResponses = teamService.findAll(); + + //then + assertThat(selectTeamResponses).hasSize(teams.size()); + } + + @Test + @DisplayName("존재하는 팀이 없을 경우, 예외가 발생한다.") + void findAll_return_empty() { + //given + given(teamRepository.findAll()).willReturn(Collections.emptyList()); + + //when, then + assertThatThrownBy(() -> teamService.findAll()) + .isInstanceOf(EntityNotFoundException.class) + .hasMessageContaining("존재하는 팀이 없습니다."); + } + + @Test + @DisplayName("식별번호에 일치하는 팀을 조회한다.") + void findById() { + //given + Team mockTeam = mock(Team.class); + given(teamRepository.findById(anyLong())).willReturn(Optional.of(mockTeam)); + + //when + Team team = teamService.findById(1L); + + //then + assertThat(team).isNotNull(); + } + + @Test + @DisplayName("식별번호에 일치하는 팀이 존재하지 않을 경우, 예외가 발생한다.") + void findById_fail_not_found_team() { + //given + given(teamRepository.findById(anyLong())).willReturn(Optional.empty()); + + //when, then + assertThatThrownBy(() -> teamService.findById(1L)) + .isInstanceOf(EntityNotFoundException.class) + .hasMessageContaining("해당하는 팀이 존재하지 않습니다"); + } +} diff --git a/src/test/java/playground/service/user/UserServiceTest.java b/src/test/java/playground/service/user/UserServiceTest.java index 24de132..2790059 100644 --- a/src/test/java/playground/service/user/UserServiceTest.java +++ b/src/test/java/playground/service/user/UserServiceTest.java @@ -7,15 +7,20 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.dao.DuplicateKeyException; -import playground.controller.user.request.CreateUserRequest; +import playground.domain.team.Team; import playground.domain.user.User; import playground.repository.user.UserRepository; +import playground.service.team.TeamService; +import playground.service.user.request.CreateUserRequest; +import javax.persistence.EntityNotFoundException; import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; @@ -23,6 +28,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static playground.domain.user.vo.JobPosition.TEAM_MEMBER; @ExtendWith(MockitoExtension.class) class UserServiceTest { @@ -30,17 +36,22 @@ class UserServiceTest { @Mock private UserRepository userRepository; + @Mock + private TeamService teamService; + @InjectMocks private UserService userService; @Test @DisplayName("사용자를 저장한다.") - void save() { + void create() { //given - CreateUserRequest createUserRequest = new CreateUserRequest("a@naver.com", "Password123!", "김성빈"); + Team team = mock(Team.class); + given(teamService.findById(anyLong())).willReturn(team); + CreateUserRequest createUserRequest = new CreateUserRequest("a@naver.com", "Password123!", "김성빈", 1L, TEAM_MEMBER.name()); //when - userService.save(createUserRequest); + userService.create(createUserRequest); //then verify(userRepository, times(1)).save(any(User.class)); @@ -48,14 +59,31 @@ void save() { @Test @DisplayName("중복된 이메일일 경우, 예외가 발생한다.") - void save_fail_duplicated_email() { + void create_fail_duplicated_email() { + //given + Team team = mock(Team.class); + given(teamService.findById(anyLong())).willReturn(team); + given(userRepository.save(any(User.class))).willThrow(new DuplicateKeyException("이메일 중복")); + CreateUserRequest createUserRequest = new CreateUserRequest("a@naver.com", "Password123!", "김성빈", 1L, TEAM_MEMBER.name()); + + //when, then + assertThatIllegalArgumentException() + .isThrownBy(() -> userService.create(createUserRequest)) + .withMessageContaining("이미 가입된 이메일입니다."); + } + + @Test + @DisplayName("팀이 존재하지 않을 경우, 에외가 발생한다.") + void create_fail_team_not_found() { //given - CreateUserRequest createUserRequest = new CreateUserRequest("a@naver.com", "Password123!", "김성빈"); + Team team = mock(Team.class); + given(teamService.findById(anyLong())).willReturn(team); given(userRepository.save(any(User.class))).willThrow(new DuplicateKeyException("이메일 중복")); + CreateUserRequest createUserRequest = new CreateUserRequest("a@naver.com", "Password123!", "김성빈", 1L, TEAM_MEMBER.name()); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> userService.save(createUserRequest)) + .isThrownBy(() -> userService.create(createUserRequest)) .withMessageContaining("이미 가입된 이메일입니다."); } @@ -78,7 +106,7 @@ void findAllById() { void findById() { //given User mockUser = mock(User.class); - given(userRepository.findById(anyLong())).willReturn(mockUser); + given(userRepository.findById(anyLong())).willReturn(Optional.of(mockUser)); //when User user = userService.findById(1L); @@ -91,11 +119,11 @@ void findById() { @DisplayName("식별번호에 일치하는 사용자가 존재하지 않을 경우, 예외가 발생한다.") void findById_fail_not_found_user() { //given - given(userRepository.findById(anyLong())).willThrow(new IllegalArgumentException("해당하는 회원이 존재하지 않습니다")); + given(userRepository.findById(anyLong())).willReturn(Optional.empty()); //when, then - assertThatIllegalArgumentException() - .isThrownBy(() -> userService.findById(1L)) - .withMessageContaining("해당하는 회원이 존재하지 않습니다"); + assertThatThrownBy(() -> userService.findById(1L)) + .isInstanceOf(EntityNotFoundException.class) + .hasMessageContaining("해당하는 회원이 존재하지 않습니다"); } }