diff --git a/BE/src/main/java/louie/hanse/issuetracker/domain/Issue.java b/BE/src/main/java/louie/hanse/issuetracker/domain/Issue.java index 01a23d840f..dffdd9252a 100644 --- a/BE/src/main/java/louie/hanse/issuetracker/domain/Issue.java +++ b/BE/src/main/java/louie/hanse/issuetracker/domain/Issue.java @@ -78,4 +78,11 @@ public boolean isClosed() { return status.isClosed(); } + public boolean isOpened() { + return status.isOpened(); + } + + public void deleteMilestone() { + this.milestone = null; + } } diff --git a/BE/src/main/java/louie/hanse/issuetracker/domain/Milestone.java b/BE/src/main/java/louie/hanse/issuetracker/domain/Milestone.java index 63a71d3dc9..4122f84a0e 100644 --- a/BE/src/main/java/louie/hanse/issuetracker/domain/Milestone.java +++ b/BE/src/main/java/louie/hanse/issuetracker/domain/Milestone.java @@ -1,6 +1,8 @@ package louie.hanse.issuetracker.domain; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; import javax.persistence.*; import java.time.LocalDate; @@ -11,6 +13,7 @@ @Getter @Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Milestone { @Id @GeneratedValue(strategy = IDENTITY) @@ -28,4 +31,22 @@ public class Milestone { public void addIssue(Issue issue) { this.issues.add(issue); } + + public Milestone(String title, String description, LocalDate completedDate) { + this.title = title; + this.description = description; + this.completedDate = completedDate; + } + + public void updateTitle(String title) { + this.title = title; + } + + public void updateDescription(String description) { + this.description = description; + } + + public void updateCompletedDate(LocalDate completedDate) { + this.completedDate = completedDate; + } } diff --git a/BE/src/main/java/louie/hanse/issuetracker/domain/Status.java b/BE/src/main/java/louie/hanse/issuetracker/domain/Status.java index b3121f864f..156c8e3a18 100644 --- a/BE/src/main/java/louie/hanse/issuetracker/domain/Status.java +++ b/BE/src/main/java/louie/hanse/issuetracker/domain/Status.java @@ -16,6 +16,10 @@ public Status reverse() { } public boolean isClosed() { - return this.equals(Status.CLOSE); + return this.equals(CLOSE); + } + + public boolean isOpened() { + return this.equals(OPEN); } } diff --git a/BE/src/main/java/louie/hanse/issuetracker/repository/MilestoneRepository.java b/BE/src/main/java/louie/hanse/issuetracker/repository/MilestoneRepository.java index cb2eaf3728..5c5a383df7 100644 --- a/BE/src/main/java/louie/hanse/issuetracker/repository/MilestoneRepository.java +++ b/BE/src/main/java/louie/hanse/issuetracker/repository/MilestoneRepository.java @@ -2,6 +2,17 @@ import louie.hanse.issuetracker.domain.Milestone; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.time.LocalDate; +import java.util.List; public interface MilestoneRepository extends JpaRepository { + + @Query("select m from Milestone as m where m.completedDate < :currentDate") + List findClosedMilestone(@Param("currentDate") LocalDate currentDate); + + @Query("select m from Milestone as m where m.completedDate > :currentDate") + List findOpenedMilestone(@Param("currentDate") LocalDate currentDate); } diff --git a/BE/src/main/java/louie/hanse/issuetracker/service/MileStoneService.java b/BE/src/main/java/louie/hanse/issuetracker/service/MileStoneService.java new file mode 100644 index 0000000000..5a1ced310f --- /dev/null +++ b/BE/src/main/java/louie/hanse/issuetracker/service/MileStoneService.java @@ -0,0 +1,56 @@ +package louie.hanse.issuetracker.service; + +import lombok.RequiredArgsConstructor; +import louie.hanse.issuetracker.domain.Issue; +import louie.hanse.issuetracker.domain.Milestone; +import louie.hanse.issuetracker.domain.Status; +import louie.hanse.issuetracker.repository.MilestoneRepository; +import louie.hanse.issuetracker.web.dto.milestone.MilestoneListResponse; +import louie.hanse.issuetracker.web.dto.milestone.MilestoneRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MileStoneService { + + private final MilestoneRepository milestoneRepository; + + @Transactional + public void register(MilestoneRequest request) { + Milestone milestone = request.toEntity(); + milestoneRepository.save(milestone); + } + + public MilestoneListResponse getMilestoneList(Status status) { + List closedMilestone = milestoneRepository.findClosedMilestone(LocalDate.now()); + List openedMilestone = milestoneRepository.findOpenedMilestone(LocalDate.now()); + if (status.isOpened()) { + return new MilestoneListResponse(openedMilestone.size(), closedMilestone.size(), openedMilestone); + } + return new MilestoneListResponse(openedMilestone.size(), closedMilestone.size(), closedMilestone); + } + + @Transactional + public void edit(Long id, MilestoneRequest request) { + Milestone milestone = milestoneRepository.findById(id).orElseThrow(IllegalStateException::new); + + milestone.updateTitle(request.getTitle()); + milestone.updateDescription(request.getDescription()); + milestone.updateCompletedDate(request.getCompletedDate()); + } + + @Transactional + public void delete(Long id) { + Milestone milestone = milestoneRepository.findById(id).orElseThrow(IllegalStateException::new); + List issues = milestone.getIssues(); + for (Issue issue : issues) { + issue.deleteMilestone(); + } + milestoneRepository.delete(milestone); + } +} diff --git a/BE/src/main/java/louie/hanse/issuetracker/web/controller/MileStoneController.java b/BE/src/main/java/louie/hanse/issuetracker/web/controller/MileStoneController.java new file mode 100644 index 0000000000..f14622382e --- /dev/null +++ b/BE/src/main/java/louie/hanse/issuetracker/web/controller/MileStoneController.java @@ -0,0 +1,36 @@ +package louie.hanse.issuetracker.web.controller; + +import lombok.RequiredArgsConstructor; +import louie.hanse.issuetracker.domain.Status; +import louie.hanse.issuetracker.service.MileStoneService; +import louie.hanse.issuetracker.web.dto.milestone.MilestoneListResponse; +import louie.hanse.issuetracker.web.dto.milestone.MilestoneRequest; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/milestone") +@RequiredArgsConstructor +public class MileStoneController { + + private final MileStoneService mileStoneService; + + @PostMapping + public void registerMilestone(@RequestBody MilestoneRequest request) { + mileStoneService.register(request); + } + + @GetMapping + public MilestoneListResponse getMilestoneList(@RequestParam("status") Status status) { + return mileStoneService.getMilestoneList(status); + } + + @PutMapping("/{id}") + public void editMilestone(@PathVariable Long id, @RequestBody MilestoneRequest request) { + mileStoneService.edit(id, request); + } + + @DeleteMapping("/{id}") + public void deleteMilestone(@PathVariable Long id) { + mileStoneService.delete(id); + } +} diff --git a/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneDetailResponse.java b/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneDetailResponse.java new file mode 100644 index 0000000000..487dfe72c0 --- /dev/null +++ b/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneDetailResponse.java @@ -0,0 +1,30 @@ +package louie.hanse.issuetracker.web.dto.milestone; + +import lombok.Getter; +import louie.hanse.issuetracker.domain.Issue; +import louie.hanse.issuetracker.domain.Milestone; + +import java.time.LocalDate; + +@Getter +public class MilestoneDetailResponse { + private Long id; + private String title; + private String description; + private LocalDate completedDate; + private long openedIssueCount; + private long closedIssueCount; + + public MilestoneDetailResponse(Milestone milestone) { + this.id = milestone.getId(); + this.title = milestone.getTitle(); + this.description = milestone.getDescription(); + this.completedDate = milestone.getCompletedDate(); + this.openedIssueCount = milestone.getIssues().stream() + .filter(Issue::isOpened) + .count(); + this.closedIssueCount = milestone.getIssues().stream() + .filter(Issue::isClosed) + .count(); + } +} diff --git a/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneListResponse.java b/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneListResponse.java new file mode 100644 index 0000000000..54b5f929cf --- /dev/null +++ b/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneListResponse.java @@ -0,0 +1,22 @@ +package louie.hanse.issuetracker.web.dto.milestone; + +import lombok.Getter; +import louie.hanse.issuetracker.domain.Milestone; + +import java.util.List; +import java.util.stream.Collectors; + +@Getter +public class MilestoneListResponse { + private int openedMileStoneCount; + private int closedMileStoneCount; + private List milestones; + + public MilestoneListResponse(int openedMileStoneCount, int closedMileStoneCount, List milestones) { + this.openedMileStoneCount = openedMileStoneCount; + this.closedMileStoneCount = closedMileStoneCount; + this.milestones = milestones.stream() + .map(MilestoneDetailResponse::new) + .collect(Collectors.toList()); + } +} diff --git a/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneRequest.java b/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneRequest.java new file mode 100644 index 0000000000..4213b8d616 --- /dev/null +++ b/BE/src/main/java/louie/hanse/issuetracker/web/dto/milestone/MilestoneRequest.java @@ -0,0 +1,19 @@ +package louie.hanse.issuetracker.web.dto.milestone; + +import lombok.Getter; +import louie.hanse.issuetracker.domain.Milestone; + +import javax.validation.constraints.NotEmpty; +import java.time.LocalDate; + +@Getter +public class MilestoneRequest { + @NotEmpty + private String title; + private String description; + private LocalDate completedDate; + + public Milestone toEntity() { + return new Milestone(title, description, completedDate); + } +}