diff --git a/http/document.http b/http/document.http index ce434f5..a92c7a1 100644 --- a/http/document.http +++ b/http/document.http @@ -17,4 +17,7 @@ Content-Type: application/json GET http://localhost:8080/api/documents/1 ### OUT BOX 문서 조회 -GET http://localhost:8080/api/documents/out?userId=1 \ No newline at end of file +GET http://localhost:8080/api/documents/out?userId=1 + +### 문서 분류 조회 +GET http://localhost:8080/api/documents/categories diff --git a/http/team.http b/http/team.http new file mode 100644 index 0000000..7ea9860 --- /dev/null +++ b/http/team.http @@ -0,0 +1,19 @@ +### 팀 저장1 +POST http://localhost:8080/api/team +Content-Type: application/json + +{ + "name": "정산시스템팀" +} + +### 팀 저장2 +POST http://localhost:8080/api/team +Content-Type: application/json + +{ +"name": "서비스개발팀" +} + + +### 전체 팀 조회 +GET http://localhost:8080/api/teams diff --git a/http/user.http b/http/user.http index 0f992a1..4df86fe 100644 --- a/http/user.http +++ b/http/user.http @@ -1,10 +1,37 @@ -### 사용자 저장 +### 팀 저장 +POST http://localhost:8080/api/team +Content-Type: application/json + +{ + "name": "정산시스템팀" +} + +### 회원 저장1 +POST http://localhost:8080/api/user +Content-Type: application/json + +{ + "name": "userA", + "password": "pa@sw**d", + "email": "userA@gmail.com", + "teamName": "정산시스템팀", + "jobPosition": "PART_MANAGER" +} + +### 회원 저장2 POST http://localhost:8080/api/user Content-Type: application/json { - "name": "userA" + "name": "userA", + "password": "pa@sw**d", + "email": "userA@gmail.com", + "teamName": "정산시스템팀", + "jobPosition": "TEAM_LEADER" } -### 사용자 1명 조회 +### 회원 1명 조회 GET http://localhost:8080/api/users/1 + +### 팀에 속한 회원 조회 +GET http://localhost:8080/api/users?teamId=1 \ No newline at end of file diff --git a/src/main/java/playground/controller/DocumentController.java b/src/main/java/playground/controller/DocumentController.java index 7c63b45..3857ad1 100644 --- a/src/main/java/playground/controller/DocumentController.java +++ b/src/main/java/playground/controller/DocumentController.java @@ -3,12 +3,15 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import playground.domain.document.Category; import playground.service.document.DocumentService; +import playground.service.document.dto.CategoryResponse; import playground.service.document.dto.DocumentRequest; import playground.service.document.dto.DocumentResponse; import playground.service.document.dto.OutboxResponse; import java.net.URI; +import java.util.Arrays; import java.util.List; @RestController @@ -18,21 +21,27 @@ public class DocumentController { private final DocumentService documentService; - @GetMapping("/documents/{id}") + @GetMapping(value = "/documents/{id}") public ResponseEntity find(@PathVariable Long id) { DocumentResponse document = documentService.findOne(id); return ResponseEntity.ok(document); } - @GetMapping("/documents/out") + @GetMapping(value = "/documents/out") public ResponseEntity> list(@RequestParam("userId") Long userId) { List outBox = documentService.findOutBox(userId); return ResponseEntity.ok(outBox); } - @PostMapping("/document") + @PostMapping(value = "/document") public ResponseEntity save(@RequestBody DocumentRequest requestDto) { DocumentResponse document = documentService.save(requestDto); return ResponseEntity.created(URI.create("/documents/" + document.getId())).body(document); } + + @GetMapping(value = "/documents/categories") + public ResponseEntity> findCategories() { + List categories = CategoryResponse.ofList(Arrays.asList(Category.values())); + return ResponseEntity.ok(categories); + } } diff --git a/src/main/java/playground/controller/TeamController.java b/src/main/java/playground/controller/TeamController.java new file mode 100644 index 0000000..d82014c --- /dev/null +++ b/src/main/java/playground/controller/TeamController.java @@ -0,0 +1,36 @@ +package playground.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import playground.service.user.TeamService; +import playground.service.user.dto.TeamRequest; +import playground.service.user.dto.TeamResponse; + +import java.net.URI; +import java.util.List; + +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor +public class TeamController { + + private final TeamService teamService; + + @PostMapping(value = "/team") + public ResponseEntity save(@RequestBody TeamRequest request) { + TeamResponse team = teamService.saveTeam(request); + return ResponseEntity.created(URI.create("/teams/" + team.getId())).body(team); + } + + @GetMapping(value = "teams") + public ResponseEntity> findUserByTeam() { + try { + List teams = teamService.findTeams(); + return ResponseEntity.ok(teams); + + } catch (RuntimeException e) { + return ResponseEntity.notFound().build(); + } + } +} diff --git a/src/main/java/playground/controller/UserController.java b/src/main/java/playground/controller/UserController.java index 3a5f72f..301473b 100644 --- a/src/main/java/playground/controller/UserController.java +++ b/src/main/java/playground/controller/UserController.java @@ -1,13 +1,16 @@ package playground.controller; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import playground.service.user.UserService; +import playground.service.user.dto.TeamUserResponse; import playground.service.user.dto.UserRequest; import playground.service.user.dto.UserResponse; +import java.net.URI; +import java.util.List; + @RestController @RequestMapping("/api") @RequiredArgsConstructor @@ -15,20 +18,31 @@ public class UserController { private final UserService userService; - @GetMapping("/users/{id}") + @PostMapping(value = "/user") + public ResponseEntity save(@RequestBody UserRequest requestDto) { + UserResponse user = userService.saveUser(requestDto); + return ResponseEntity.created(URI.create("users/" + user.getId())).body(user); + } + + @GetMapping(value = "/users/{id}") public ResponseEntity find(@PathVariable Long id) { try { UserResponse user = userService.findOne(id); - return new ResponseEntity<>(user, HttpStatus.OK); + return ResponseEntity.ok(user); } catch (RuntimeException e) { return ResponseEntity.notFound().build(); } } - @PostMapping("/user") - public ResponseEntity save(@RequestBody UserRequest requestDto) { - UserResponse user = userService.save(requestDto); - return new ResponseEntity<>(user, HttpStatus.OK); + @GetMapping(value = "/users") + public ResponseEntity> findByTeam(@RequestParam("teamId") Long id) { + try { + List users = userService.findUserByTeam(id); + return ResponseEntity.ok(users); + + } catch (RuntimeException e) { + return ResponseEntity.notFound().build(); + } } } diff --git a/src/main/java/playground/domain/document/Document.java b/src/main/java/playground/domain/document/Document.java index db1f568..e6abe34 100644 --- a/src/main/java/playground/domain/document/Document.java +++ b/src/main/java/playground/domain/document/Document.java @@ -12,6 +12,7 @@ import java.util.List; import static javax.persistence.CascadeType.ALL; +import static javax.persistence.ConstraintMode.NO_CONSTRAINT; import static javax.persistence.FetchType.LAZY; import static javax.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; @@ -35,7 +36,7 @@ public class Document extends BaseTimeEntity { private String contents; @ManyToOne(fetch = LAZY) - @JoinColumn(name = "drafter_id", foreignKey = @ForeignKey(name = "fk_document_drafter")) + @JoinColumn(name = "drafter_id", foreignKey = @ForeignKey(value = NO_CONSTRAINT)) private User drafter; @Enumerated(EnumType.STRING) @@ -54,8 +55,15 @@ public Document(String title, Category category, String contents, User drafter) this.documentApprovals = new ArrayList<>(); } - public void addDocumentApprovals(DocumentApproval documentApproval) { - this.documentApprovals.add(documentApproval); - documentApproval.setDocument(this); + public void createApprovals(List aprovers) { + for (int i = 0; i < aprovers.size(); i++) { + DocumentApproval approval = DocumentApproval.builder() + .approver(aprovers.get(i)) + .approvalOrder(i + 1) + .build(); + + approval.upDateDocument(this); + this.documentApprovals.add(approval); + } } } diff --git a/src/main/java/playground/domain/document/DocumentRepository.java b/src/main/java/playground/domain/document/DocumentRepository.java index 4a476a6..d95367d 100644 --- a/src/main/java/playground/domain/document/DocumentRepository.java +++ b/src/main/java/playground/domain/document/DocumentRepository.java @@ -15,4 +15,5 @@ public interface DocumentRepository extends JpaRepository { "on d.id = a.document.id " + "where a.approver.id = :userId order by a.insertDate asc") List findOutBox(@Param(value = "userId") Long userId); + } diff --git a/src/main/java/playground/domain/document/approval/DocumentApproval.java b/src/main/java/playground/domain/document/approval/DocumentApproval.java index 9425124..afd9efc 100644 --- a/src/main/java/playground/domain/document/approval/DocumentApproval.java +++ b/src/main/java/playground/domain/document/approval/DocumentApproval.java @@ -1,13 +1,16 @@ package playground.domain.document.approval; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import playground.domain.document.BaseTimeEntity; import playground.domain.document.Document; +import playground.domain.user.Team; import playground.domain.user.User; import javax.persistence.*; +import static javax.persistence.ConstraintMode.NO_CONSTRAINT; import static javax.persistence.FetchType.LAZY; import static javax.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; @@ -22,11 +25,11 @@ public class DocumentApproval extends BaseTimeEntity { private Long id; @ManyToOne(fetch = LAZY) - @JoinColumn(name = "document_id", foreignKey = @ForeignKey(name = "fk_approval_document")) + @JoinColumn(name = "document_id", foreignKey = @ForeignKey(value = NO_CONSTRAINT)) private Document document; - @OneToOne - @JoinColumn(name = "approver_id", foreignKey = @ForeignKey(name = "fk_document_approver")) + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "approver_id", foreignKey = @ForeignKey(value = NO_CONSTRAINT)) private User approver; @Enumerated(EnumType.STRING) @@ -37,17 +40,18 @@ public class DocumentApproval extends BaseTimeEntity { @Lob private String approvalComment; - private DocumentApproval(User approver, int approvalOrder) { + @Builder + public DocumentApproval(User approver, int approvalOrder) { this.approver = approver; this.approvalState = DRAFTING; this.approvalOrder = approvalOrder; } - public static DocumentApproval create(User approver, int approvalOrder) { - return new DocumentApproval(approver, approvalOrder); + public void upDateDocument(Document document) { + this.document = document; } - public void setDocument(Document document) { - this.document = document; + public Team getTeam() { + return approver.getTeam(); } } diff --git a/src/main/java/playground/domain/user/JobPosition.java b/src/main/java/playground/domain/user/JobPosition.java new file mode 100644 index 0000000..555163b --- /dev/null +++ b/src/main/java/playground/domain/user/JobPosition.java @@ -0,0 +1,15 @@ +package playground.domain.user; + +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/domain/user/Team.java b/src/main/java/playground/domain/user/Team.java new file mode 100644 index 0000000..3e3ed97 --- /dev/null +++ b/src/main/java/playground/domain/user/Team.java @@ -0,0 +1,33 @@ +package playground.domain.user; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import playground.domain.document.BaseTimeEntity; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PROTECTED; + +@Getter +@Entity +@RequiredArgsConstructor(access = PROTECTED) +public class Team extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @Column(unique = true) + private String name; + + @OneToMany(mappedBy = "team", cascade = ALL, orphanRemoval = true) + private List users = new ArrayList<>(); + + public Team(String name) { + this.name = name; + } +} diff --git a/src/main/java/playground/domain/user/TeamRepository.java b/src/main/java/playground/domain/user/TeamRepository.java new file mode 100644 index 0000000..c133519 --- /dev/null +++ b/src/main/java/playground/domain/user/TeamRepository.java @@ -0,0 +1,11 @@ +package playground.domain.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TeamRepository extends JpaRepository { + + Team findTeamByName(String name); + +} diff --git a/src/main/java/playground/domain/user/User.java b/src/main/java/playground/domain/user/User.java index be5d6d0..251c0c5 100644 --- a/src/main/java/playground/domain/user/User.java +++ b/src/main/java/playground/domain/user/User.java @@ -1,5 +1,6 @@ package playground.domain.user; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import playground.domain.document.BaseTimeEntity; @@ -8,6 +9,9 @@ import javax.persistence.*; import java.util.List; +import static javax.persistence.ConstraintMode.NO_CONSTRAINT; +import static javax.persistence.EnumType.STRING; +import static javax.persistence.FetchType.EAGER; import static javax.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; @@ -23,10 +27,32 @@ public class User extends BaseTimeEntity { @Column(length = 100) private String name; + @Column(nullable = false) + private String password; + + @Column + private String email; + + @ManyToOne(fetch = EAGER) + @JoinColumn(name = "team_id", foreignKey = @ForeignKey(value = NO_CONSTRAINT)) + private Team team; + + @Enumerated(value = STRING) + private JobPosition jobPosition; + @OneToMany(mappedBy = "drafter") private List documents; - public User(String name) { + @Builder + public User(String name, String password, String email, Team team, JobPosition jobPosition) { this.name = name; + this.password = password; + this.email = email; + this.team = team; + this.jobPosition = jobPosition; + } + + public String getTeamName() { + return team.getName(); } } diff --git a/src/main/java/playground/domain/user/UserRepository.java b/src/main/java/playground/domain/user/UserRepository.java index 5d1bfcf..2a86118 100644 --- a/src/main/java/playground/domain/user/UserRepository.java +++ b/src/main/java/playground/domain/user/UserRepository.java @@ -3,7 +3,11 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface UserRepository extends JpaRepository { + List findUserByTeam(Team team); + } diff --git a/src/main/java/playground/exception/NotFoundException.java b/src/main/java/playground/exception/NotFoundException.java index 781daff..7d2f904 100644 --- a/src/main/java/playground/exception/NotFoundException.java +++ b/src/main/java/playground/exception/NotFoundException.java @@ -9,11 +9,7 @@ public NotFoundException(String message) { super(message); } - public NotFoundException(String message, Throwable cause) { - super(message, cause); - } - - public NotFoundException(Throwable cause) { - super(cause); + public static NotFoundException error(String message) { + return new NotFoundException(message); } } diff --git a/src/main/java/playground/service/document/DocumentService.java b/src/main/java/playground/service/document/DocumentService.java index 79f38aa..a19906b 100644 --- a/src/main/java/playground/service/document/DocumentService.java +++ b/src/main/java/playground/service/document/DocumentService.java @@ -9,11 +9,15 @@ import playground.domain.user.User; import playground.domain.user.UserRepository; import playground.exception.NotFoundException; +import playground.service.document.dto.ApprovalResponse; import playground.service.document.dto.DocumentRequest; import playground.service.document.dto.DocumentResponse; import playground.service.document.dto.OutboxResponse; +import playground.service.user.dto.TeamUserResponse; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Service @@ -27,42 +31,56 @@ public class DocumentService { public DocumentResponse findOne(Long documentId) { Document document = documentRepository.findById(documentId) .orElseThrow(() -> new NotFoundException("문서를 찾을 수 없습니다.")); - return DocumentResponse.convertFrom(document); + + List approvals = getApprovalResponses(document); + TeamUserResponse drafter = TeamUserResponse.of(document.getDrafter()); + + return DocumentResponse.of(document, drafter, approvals); + } + + private List getApprovalResponses(Document document) { + List documentApprovals = document.getDocumentApprovals(); + return documentApprovals.stream() + .map(approval -> ApprovalResponse.of(approval, approval.getApprover())) + .collect(Collectors.toList()); } public List findOutBox(Long userId) { List outBox = documentRepository.findOutBox(userId); return outBox.stream() - .map(OutboxResponse::convertFrom) + .map(OutboxResponse::of) .collect(Collectors.toList()); } @Transactional - public DocumentResponse save(DocumentRequest dto) { - User drafter = findUser(dto.getDrafterId()); - Document document = dto.toDocument(drafter); + public DocumentResponse save(DocumentRequest request) { + User drafter = findUser(request.getDrafterId()); + List aprovers = createApproversInOrder(request); + Document document = request.toDocument(drafter, aprovers); - setDocumentApproval(dto.getApproverIds(), document); documentRepository.save(document); - return DocumentResponse.convertFrom(document); + return DocumentResponse.of(document, TeamUserResponse.of(drafter)); } - private User findUser(Long userId) { - return userRepository.findById(userId) - .orElseThrow(() -> new NotFoundException("문서 기안 사용자를 찾을 수 없습니다.")); - } + private List createApproversInOrder(DocumentRequest request) { + Map approvers = findApprovers(request); - private void setDocumentApproval(List approvalIds, Document document) { - int approvalOrder = 0; - for (Long approvalId : approvalIds) { - User approver = findApprover(approvalId); - DocumentApproval documentApproval = DocumentApproval.create(approver, approvalOrder++); - document.addDocumentApprovals(documentApproval); + List orderApprovers = new ArrayList<>(); + for (Long approverId : request.getApproverIds()) { + orderApprovers.add(approvers.get(approverId)); } + + return orderApprovers; + } + + private Map findApprovers(DocumentRequest request) { + List approvers = userRepository.findAllById(request.getApproverIds()); + return approvers.stream() + .collect(Collectors.toMap(User::getId, user -> user)); } - private User findApprover(Long approvalId) { - return userRepository.findById(approvalId) - .orElseThrow(() -> new NotFoundException("문서 결재 사용자를 찾을 수 없습니다.")); + private User findUser(Long userId) { + return userRepository.findById(userId) + .orElseThrow(() -> new NotFoundException("문서 기안 사용자를 찾을 수 없습니다.")); } } diff --git a/src/main/java/playground/service/document/dto/ApprovalResponse.java b/src/main/java/playground/service/document/dto/ApprovalResponse.java new file mode 100644 index 0000000..a6e181f --- /dev/null +++ b/src/main/java/playground/service/document/dto/ApprovalResponse.java @@ -0,0 +1,33 @@ +package playground.service.document.dto; + +import lombok.Builder; +import lombok.Getter; +import playground.domain.document.approval.ApprovalState; +import playground.domain.document.approval.DocumentApproval; +import playground.domain.user.User; + +@Getter +@Builder +public class ApprovalResponse { + + private String approverTeamName; + private String approverName; + private ApprovalState approvalState; + private int approvalOrder; + private String approvalComment; + private String approvalStateText; + + public static ApprovalResponse of(DocumentApproval approval, User approver) { + return ApprovalResponse.builder() + .approverTeamName(approver.getTeamName()) + .approverName(approver.getName()) + .approvalState(approval.getApprovalState()) + .approvalOrder(approval.getApprovalOrder()) + .approvalComment(approval.getApprovalComment()) + .build(); + } + + public String getApprovalStateText() { + return approvalState.getText(); + } +} diff --git a/src/main/java/playground/service/document/dto/CategoryResponse.java b/src/main/java/playground/service/document/dto/CategoryResponse.java new file mode 100644 index 0000000..4076d1a --- /dev/null +++ b/src/main/java/playground/service/document/dto/CategoryResponse.java @@ -0,0 +1,29 @@ +package playground.service.document.dto; + +import lombok.Builder; +import lombok.Getter; +import playground.domain.document.Category; + +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@Builder +public class CategoryResponse { + private String value; + private String text; + + public static CategoryResponse of(Category category) { + return CategoryResponse.builder() + .value(category.name()) + .text(category.getText()) + .build(); + + } + + public static List ofList(List categories) { + return categories.stream() + .map(CategoryResponse::of) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/playground/service/document/dto/DocumentRequest.java b/src/main/java/playground/service/document/dto/DocumentRequest.java index 57bf98b..7180413 100644 --- a/src/main/java/playground/service/document/dto/DocumentRequest.java +++ b/src/main/java/playground/service/document/dto/DocumentRequest.java @@ -18,12 +18,15 @@ public class DocumentRequest { private Long drafterId; private List approverIds; - public Document toDocument(User drafter) { - return Document.builder() + public Document toDocument(User drafter, List aprovers) { + Document document = Document.builder() .title(title) .category(category) .contents(contents) .drafter(drafter) .build(); + + document.createApprovals(aprovers); + return document; } } diff --git a/src/main/java/playground/service/document/dto/DocumentResponse.java b/src/main/java/playground/service/document/dto/DocumentResponse.java index 855f56f..1fba7cc 100644 --- a/src/main/java/playground/service/document/dto/DocumentResponse.java +++ b/src/main/java/playground/service/document/dto/DocumentResponse.java @@ -5,6 +5,9 @@ import playground.domain.document.Category; import playground.domain.document.Document; import playground.domain.document.approval.ApprovalState; +import playground.service.user.dto.TeamUserResponse; + +import java.util.List; @Getter @Builder @@ -14,18 +17,29 @@ public class DocumentResponse { private String title; private Category category; private String contents; - private Long userId; + private TeamUserResponse drafter; private ApprovalState approvalState; - private String userName; + private List approvers; + + public static DocumentResponse of(Document document, TeamUserResponse drafter) { + return DocumentResponse.builder() + .id(document.getId()) + .title(document.getTitle()) + .category(document.getCategory()) + .contents(document.getContents()) + .drafter(drafter) + .approvalState(document.getApprovalState()) + .build(); + } - public static DocumentResponse convertFrom(Document document) { + public static DocumentResponse of(Document document, TeamUserResponse drafter, List approvals) { return DocumentResponse.builder() .id(document.getId()) .title(document.getTitle()) .category(document.getCategory()) .contents(document.getContents()) - .userId(document.getDrafter().getId()) - .userName(document.getDrafter().getName()) + .drafter(drafter) + .approvers(approvals) .approvalState(document.getApprovalState()) .build(); } diff --git a/src/main/java/playground/service/document/dto/OutboxResponse.java b/src/main/java/playground/service/document/dto/OutboxResponse.java index 44652c7..72bb50a 100644 --- a/src/main/java/playground/service/document/dto/OutboxResponse.java +++ b/src/main/java/playground/service/document/dto/OutboxResponse.java @@ -15,7 +15,7 @@ public class OutboxResponse { private Category category; private ApprovalState approvalState; - public static OutboxResponse convertFrom(Document document) { + public static OutboxResponse of(Document document) { return OutboxResponse.builder() .id(document.getId()) .title(document.getTitle()) diff --git a/src/main/java/playground/service/user/TeamService.java b/src/main/java/playground/service/user/TeamService.java new file mode 100644 index 0000000..24a4b8c --- /dev/null +++ b/src/main/java/playground/service/user/TeamService.java @@ -0,0 +1,35 @@ +package playground.service.user; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import playground.domain.user.Team; +import playground.domain.user.TeamRepository; +import playground.service.user.dto.TeamRequest; +import playground.service.user.dto.TeamResponse; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class TeamService { + + private final TeamRepository teamRepository; + + public List findTeams() { + List teams = teamRepository.findAll(); + return TeamResponse.ofList(teams); + } + + @Transactional + public TeamResponse saveTeam(TeamRequest request) { + Team team = teamRepository.save(request.toTeam()); + return TeamResponse.of(team); + } + + public List findAll() { + List team = teamRepository.findAll(); + return TeamResponse.ofList(team); + } +} diff --git a/src/main/java/playground/service/user/UserService.java b/src/main/java/playground/service/user/UserService.java index 83c0ca0..c792333 100644 --- a/src/main/java/playground/service/user/UserService.java +++ b/src/main/java/playground/service/user/UserService.java @@ -3,28 +3,41 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import playground.domain.user.User; -import playground.domain.user.UserRepository; -import playground.exception.NotFoundException; +import playground.domain.user.*; +import playground.service.user.dto.TeamUserResponse; import playground.service.user.dto.UserRequest; import playground.service.user.dto.UserResponse; +import java.util.List; + +import static playground.exception.NotFoundException.error; + @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class UserService { private final UserRepository userRepository; + private final TeamRepository teamRepository; + + @Transactional + public UserResponse saveUser(UserRequest request) { + Team team = teamRepository.findTeamByName(request.getTeamName()); + User user = userRepository.save(request.toUser(team, JobPosition.valueOf(request.getJobPosition()))); + return UserResponse.of(user); + } public UserResponse findOne(Long userId) { User user = userRepository.findById(userId) - .orElseThrow(() -> new NotFoundException("사용자를 찾을 수 없습니다.")); - return UserResponse.convertFrom(user); + .orElseThrow(() -> error("사용자를 찾을 수 없습니다.")); + return UserResponse.of(user); } - @Transactional - public UserResponse save(UserRequest dto) { - User user = userRepository.save(dto.toUser()); - return findOne(user.getId()); + public List findUserByTeam(long id) { + Team team = teamRepository.findById(id) + .orElseThrow(() -> error("팀을 찾을 수 없습니다.")); + + List users = userRepository.findUserByTeam(team); + return TeamUserResponse.ofList(users); } } diff --git a/src/main/java/playground/service/user/dto/TeamRequest.java b/src/main/java/playground/service/user/dto/TeamRequest.java new file mode 100644 index 0000000..9e1a720 --- /dev/null +++ b/src/main/java/playground/service/user/dto/TeamRequest.java @@ -0,0 +1,20 @@ +package playground.service.user.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import playground.domain.user.Team; + +@Getter +@NoArgsConstructor +public class TeamRequest { + + private String name; + + public TeamRequest(String name) { + this.name = name; + } + + public Team toTeam() { + return new Team(name); + } +} diff --git a/src/main/java/playground/service/user/dto/TeamResponse.java b/src/main/java/playground/service/user/dto/TeamResponse.java new file mode 100644 index 0000000..4d043a5 --- /dev/null +++ b/src/main/java/playground/service/user/dto/TeamResponse.java @@ -0,0 +1,28 @@ +package playground.service.user.dto; + +import lombok.Builder; +import lombok.Getter; +import playground.domain.user.Team; + +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@Builder +public class TeamResponse { + private Long id; + private String name; + + public static TeamResponse of(Team team) { + return TeamResponse.builder() + .id(team.getId()) + .name(team.getName()) + .build(); + } + + public static List ofList(List teams) { + return teams.stream() + .map(TeamResponse::of) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/playground/service/user/dto/TeamUserResponse.java b/src/main/java/playground/service/user/dto/TeamUserResponse.java new file mode 100644 index 0000000..342d9f5 --- /dev/null +++ b/src/main/java/playground/service/user/dto/TeamUserResponse.java @@ -0,0 +1,38 @@ +package playground.service.user.dto; + +import lombok.Builder; +import lombok.Getter; +import playground.domain.user.JobPosition; +import playground.domain.user.User; + +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@Builder +public class TeamUserResponse { + private Long id; + private JobPosition jobPosition; + private String teamName; + private String name; + private String jobPositionText; + + public static TeamUserResponse of(User user) { + return TeamUserResponse.builder() + .id(user.getId()) + .jobPosition(user.getJobPosition()) + .teamName(user.getTeamName()) + .name(user.getName()) + .build(); + } + + public static List ofList(List users) { + return users.stream() + .map(TeamUserResponse::of) + .collect(Collectors.toList()); + } + + public String getJobPositionText() { + return jobPosition.getText(); + } +} diff --git a/src/main/java/playground/service/user/dto/UserRequest.java b/src/main/java/playground/service/user/dto/UserRequest.java index f822eef..8066cf2 100644 --- a/src/main/java/playground/service/user/dto/UserRequest.java +++ b/src/main/java/playground/service/user/dto/UserRequest.java @@ -1,8 +1,9 @@ package playground.service.user.dto; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import playground.domain.user.JobPosition; +import playground.domain.user.Team; import playground.domain.user.User; @Getter @@ -10,13 +11,26 @@ public class UserRequest { private String name; + private String password; + private String email; + private String teamName; + private String jobPosition; - @Builder - public UserRequest(String name) { + public UserRequest(String name, String password, String email, String teamName, String jobPosition) { this.name = name; + this.password = password; + this.email = email; + this.teamName = teamName; + this.jobPosition = jobPosition; } - public User toUser() { - return new User(name); + public User toUser(Team team, JobPosition jobPosition) { + return User.builder() + .name(name) + .password(password) + .email(email) + .team(team) + .jobPosition(jobPosition) + .build(); } } \ No newline at end of file diff --git a/src/main/java/playground/service/user/dto/UserResponse.java b/src/main/java/playground/service/user/dto/UserResponse.java index d169a3a..df3e9ab 100644 --- a/src/main/java/playground/service/user/dto/UserResponse.java +++ b/src/main/java/playground/service/user/dto/UserResponse.java @@ -14,7 +14,7 @@ public class UserResponse { private LocalDateTime insertDate; private LocalDateTime updateDate; - public static UserResponse convertFrom(User user) { + public static UserResponse of(User user) { return UserResponse.builder() .id(user.getId()) .name(user.getName()) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index fdb3aec..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,13 +0,0 @@ -# H2 DB 설정 -spring.h2.console.enabled=true -spring.h2.console.path=/h2 -spring.datasource.username=sa -spring.datasource.password= -spring.datasource.url=jdbc:h2:mem:playground -spring.datasource.driverClassName=org.h2.Driver -spring.jpa.defer-datasource-initialization=true -# Hibernate 설정 -spring.jpa.hibernate.ddl-auto=create -spring.jpa.properties.hibernate.format_sql=true -logging.level.org.hibernate.SQL=debug -logging.level.org.hibernate.type=trace \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..b1f5697 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,23 @@ +spring: + h2: + console: + enabled: true + datasource: + url: jdbc:h2:mem:playground + username: sa + password: + driver-class-name: org.h2.Driver + jpa: + show-sql: true + hibernate: + ddl-auto: create + defer-datasource-initialization: true + properties: + hibernate: + format_sql: true + +logging.level: + org.hibernate.SQL: debug + org: + hibernate: + type: trace diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 1e1c390..b31a440 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,15 +1,17 @@ -insert into user(id, name, insert_date) -values (1, 'user1', '2021-11-17 15:50:57'), - (2, 'user2', '2021-11-17 15:50:57'), - (3, 'user3', '2021-11-17 15:50:57'); +insert into team(id, name, insert_date) +values (1, '정산시스템팀', '2021-10-17 15:50:57'), + (2, '서비스개발팀', '2021-10-17 15:50:57'); +insert into user(id, name, password, email, team_id, job_position, insert_date) +values (1, 'user1', 'pa@sw**d', 'user1@gmail.com', 1, 'TEAM_LEADER', '2021-11-17 15:50:57'), + (2, 'user2', 'pa@sw**d', 'user2@gmail.com', 1, 'TEAM_MEMBER', '2021-11-17 15:50:57'), + (3, 'user3', 'pa@sw**d', 'user3@gmail.com', 2, 'PART_MANAGER', '2021-11-17 15:50:57'); insert into document(id, title, category, contents, drafter_id, approval_state, insert_date) values (1, '문서1', 'OPERATING_EXPENSES', '운영비 결재 요청드립니다.', 1, 'APPROVED', '2021-11-17 16:50:57'), (2, '문서2', 'EDUCATION', '사외교육비 결재 요청드립니다.', 2, 'DRAFTING', '2021-11-17 16:50:57'), (3, '문서3', 'EDUCATION', '사외교육비 결재 요청드립니다.', 3, 'CANCELED', '2021-11-17 16:50:57'); - insert into document_approval(id, approver_id, document_id, approval_state, approval_order, approval_comment, insert_date) values (1, 1, 1, 'APPROVED', 1, '', '2021-11-17 17:50:57'), (2, 2, 1, 'APPROVED', 2, '', '2021-11-17 18:50:57'), diff --git a/src/test/java/learning/DocumentTest.java b/src/test/java/learning/DocumentTest.java index 830db72..7a1055e 100644 --- a/src/test/java/learning/DocumentTest.java +++ b/src/test/java/learning/DocumentTest.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.test.context.jdbc.Sql; import java.util.Arrays; import java.util.Collections; @@ -11,6 +12,7 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +@Sql("data.sql") class DocumentTest { @DisplayName("문서 생성하기") diff --git a/src/test/java/playground/domain/document/DocumentRepositoryTest.java b/src/test/java/playground/domain/document/DocumentRepositoryTest.java new file mode 100644 index 0000000..92971b8 --- /dev/null +++ b/src/test/java/playground/domain/document/DocumentRepositoryTest.java @@ -0,0 +1,71 @@ +package playground.domain.document; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import playground.domain.user.User; +import playground.domain.user.UserRepository; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.tuple; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static playground.domain.document.approval.ApprovalState.DRAFTING; + +@DataJpaTest +class DocumentRepositoryTest { + + @Autowired + private UserRepository userRepository; + @Autowired + private DocumentRepository documentRepository; + + private User user1; + private User user2; + + private Document document; + + @BeforeEach + void setUp() { + user1 = userRepository.save(createUser("user1", "pa@sw**d", "user1@gmail.com")); + user2 = userRepository.save(createUser("user2", "pa@sw**d", "user2@gmail.com")); + + document = documentRepository.save(createDocument("문서 제목", "제출합니다.")); + } + + @Test + void findOutBoxBy() { + //given + List approvers = Arrays.asList(user1, user2); + document.createApprovals(approvers); + + //when + List outBox = documentRepository.findOutBox(user2.getId()); + + //then + assertThat(outBox.size()).isEqualTo(1); + assertThat(outBox).extracting("title", "approvalState") + .containsExactly( + tuple("문서 제목", DRAFTING) + ); + } + + private Document createDocument(String title, String contents) { + return Document.builder() + .drafter(user1) + .title(title) + .category(Category.PRODUCT_PURCHASING) + .contents(contents) + .build(); + } + + private User createUser(String name, String password, String email) { + return User.builder() + .name(name) + .password(password) + .email(email) + .build(); + } +} \ No newline at end of file diff --git a/src/test/java/playground/service/document/DocumentServiceTest.java b/src/test/java/playground/service/document/DocumentServiceTest.java index 025778f..712b3f8 100644 --- a/src/test/java/playground/service/document/DocumentServiceTest.java +++ b/src/test/java/playground/service/document/DocumentServiceTest.java @@ -1,80 +1,87 @@ package playground.service.document; +import org.junit.jupiter.api.AfterEach; 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.autoconfigure.orm.jpa.DataJpaTest; -import playground.domain.document.Category; -import playground.domain.document.Document; -import playground.domain.document.DocumentRepository; -import playground.domain.document.approval.DocumentApproval; -import playground.domain.user.User; -import playground.domain.user.UserRepository; +import org.springframework.boot.test.context.SpringBootTest; +import playground.domain.user.*; +import playground.service.document.dto.DocumentRequest; +import playground.service.document.dto.DocumentResponse; -import java.util.List; -import java.util.Optional; +import java.util.Arrays; -import static org.assertj.core.api.AssertionsForClassTypes.tuple; -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static playground.domain.document.Category.EDUCATION; import static playground.domain.document.approval.ApprovalState.DRAFTING; +import static playground.domain.user.JobPosition.*; -@DataJpaTest +@SpringBootTest class DocumentServiceTest { @Autowired - private UserRepository userRepository; + private DocumentService documentService; + @Autowired - private DocumentRepository documentRepository; + private UserRepository userRepository; - private User user1; - private User user2; + @Autowired + private TeamRepository teamRepository; - private Document document; + private User dafter; + private User approver1; + private User approver2; @BeforeEach void setUp() { - user1 = userRepository.save(new User("user1")); - user2 = userRepository.save(new User("user2")); - - document = documentRepository.save(createDocument("문서 제목", "제출합니다.")); + Team team = teamRepository.save(new Team("새로운팀")); + dafter = userRepository.save(createUser("user1", "pa@sw**d", "user1@gmail.com", team, TEAM_MEMBER)); + approver1 = userRepository.save(createUser("user2", "pa@sw**d", "user2@gmail.com", team, TEAM_LEADER)); + approver2 = userRepository.save(createUser("user3", "pa@sw**d", "user3@gmail.com", team, PART_MANAGER)); } - @Test - void findById() { - //when - Optional findDocument = documentRepository.findById(document.getId()); - - //then - assertThat(findDocument).hasValueSatisfying(actual -> - assertThat(actual) - .extracting("title", "contents", "category", "approvalState") - .containsExactly(document.getTitle(), document.getContents(), document.getCategory(), document.getApprovalState()) - ); + @AfterEach + public void cleanUp() { + teamRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); } @Test - void findOutBoxBy() { - //given - document.addDocumentApprovals(DocumentApproval.create(user1, 1)); - document.addDocumentApprovals(DocumentApproval.create(user2, 2)); + @DisplayName("문서 단건 조회") + void findOne() { + // given + DocumentRequest request = DocumentRequest.builder() + .title("문서 제목") + .category(EDUCATION) + .contents("제출합니다.") + .drafterId(dafter.getId()) + .approverIds(Arrays.asList(approver1.getId(), approver2.getId())) + .build(); + + DocumentResponse document = documentService.save(request); - //when - List outBox = documentRepository.findOutBox(user2.getId()); + // when + DocumentResponse response = documentService.findOne(document.getId()); - //then - assertThat(outBox.size()).isEqualTo(1); - assertThat(outBox).extracting("title", "approvalState") - .containsExactly( - tuple("문서 제목", DRAFTING) - ); + // then + assertThat(response) + .extracting("title", "contents", "drafter.name", "category", "approvalState") + .containsExactly("문서 제목", "제출합니다.", "user1", EDUCATION, DRAFTING); + assertThat(response.getApprovers()) + .extracting("approverTeamName", "approverName", "approvalOrder", "approvalStateText") + .containsExactly(tuple("새로운팀", "user2", 1, "결재중"), + tuple("새로운팀", "user3", 2, "결재중")); } - private Document createDocument(String title, String contents) { - return Document.builder() - .drafter(user1) - .title(title) - .category(Category.PRODUCT_PURCHASING) - .contents(contents) + private User createUser(String name, String password, String email, Team team, JobPosition jobPosition) { + return User.builder() + .name(name) + .password(password) + .email(email) + .team(team) + .jobPosition(jobPosition) .build(); } -} \ No newline at end of file +} diff --git a/src/test/java/playground/service/user/TeamServiceTest.java b/src/test/java/playground/service/user/TeamServiceTest.java new file mode 100644 index 0000000..9daab13 --- /dev/null +++ b/src/test/java/playground/service/user/TeamServiceTest.java @@ -0,0 +1,45 @@ +package playground.service.user; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import playground.domain.user.Team; +import playground.domain.user.TeamRepository; +import playground.service.user.dto.TeamResponse; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class TeamServiceTest { + + @Autowired + private TeamService teamService; + + @Autowired + private TeamRepository teamRepository; + + @AfterEach + public void cleanUp() { + teamRepository.deleteAllInBatch(); + } + + @Test + @DisplayName("전체 팀 조회") + void findTeams() { + // given + teamRepository.save(new Team("정산시스템팀")); + teamRepository.save(new Team("서비스개발팀")); + + // when + List teams = teamService.findAll(); + + // then + assertThat(teams).hasSize(2); + assertThat(teams).extracting("name") + .containsExactly("정산시스템팀", "서비스개발팀"); + } +} diff --git a/src/test/java/playground/service/user/UserRepositoryTest.java b/src/test/java/playground/service/user/UserRepositoryTest.java deleted file mode 100644 index 3c7a6e0..0000000 --- a/src/test/java/playground/service/user/UserRepositoryTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package playground.service.user; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import playground.domain.user.User; -import playground.domain.user.UserRepository; - -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; - -@DataJpaTest -class UserRepositoryTest { - - @Autowired - private UserRepository userRepository; - - private User user; - - @BeforeEach - void setUp() { - user = userRepository.save(new User("user1")); - } - - @Test - void findById() { - //when - Optional findUser = userRepository.findById(user.getId()); - - //then - assertThat(findUser.isPresent()).isTrue(); - } - - @Test - void save() { - //then - assertThat(user.getName()).isEqualTo("user1"); - } -} \ No newline at end of file diff --git a/src/test/java/playground/service/user/UserServiceTest.java b/src/test/java/playground/service/user/UserServiceTest.java new file mode 100644 index 0000000..cc5a6e7 --- /dev/null +++ b/src/test/java/playground/service/user/UserServiceTest.java @@ -0,0 +1,65 @@ +package playground.service.user; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import playground.domain.user.*; +import playground.service.user.dto.TeamUserResponse; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static playground.domain.user.JobPosition.TEAM_LEADER; +import static playground.domain.user.JobPosition.TEAM_MEMBER; + +@SpringBootTest +class UserServiceTest { + + @Autowired + private UserService userService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private TeamRepository teamRepository; + + @AfterEach + public void cleanUp() { + teamRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + } + + @Test + @DisplayName("팀에 속한 회원 조회") + void findUSerByTeam() { + // given + Team team = teamRepository.save(new Team("새로운팀")); + + User user1 = createUser("user1", "pa@sw**d", "user1@gmail.com", team, TEAM_LEADER); + User user2 = createUser("user2", "pa@sw**d", "user2@gmail.com", team, TEAM_MEMBER); + userRepository.save(user1); + userRepository.save(user2); + + // when + List users = userService.findUserByTeam(team.getId()); + + // then + assertThat(users).extracting("name", "teamName", "jobPositionText") + .containsExactly(tuple("user1", "새로운팀", "팀장"), + tuple("user2", "새로운팀", "팀원")); + } + + private User createUser(String name, String password, String email, Team team, JobPosition jobPosition) { + return User.builder() + .name(name) + .password(password) + .email(email) + .team(team) + .jobPosition(jobPosition) + .build(); + } +} diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties deleted file mode 100644 index 829ee47..0000000 --- a/src/test/resources/application-test.properties +++ /dev/null @@ -1,10 +0,0 @@ -# H2 DB 설정 -spring.h2.console.enabled=true -spring.h2.console.path=/h2 -spring.datasource.username=sa -spring.datasource.password= -spring.datasource.url=jdbc:h2:mem:playground-test -spring.datasource.driverClassName=org.h2.Driver -# Hibernate 설정 -spring.jpa.hibernate.ddl-auto=create -spring.jpa.properties.hibernate.format_sql=true \ No newline at end of file diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..f9ef1e5 --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,24 @@ +spring: + h2: + console: + enabled: true + datasource: + url: jdbc:h2:mem:playground-test + username: sa + password: + driver-class-name: org.h2.Driver + + jpa: + show-sql: true + hibernate: + ddl-auto: create + defer-datasource-initialization: true + properties: + hibernate: + format_sql: true + +logging.level: + org.hibernate.SQL: debug + org: + hibernate: + type: trace