diff --git a/be/src/main/java/com/issuetrackermax/controller/milestone/dto/request/MilestoneModifyRequest.java b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/request/MilestoneModifyRequest.java new file mode 100644 index 000000000..8b44d12b9 --- /dev/null +++ b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/request/MilestoneModifyRequest.java @@ -0,0 +1,22 @@ +package com.issuetrackermax.controller.milestone.dto.request; + +import java.time.LocalDateTime; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class MilestoneModifyRequest { + private String name; + private String description; + private LocalDateTime dueDate; + + @Builder + public MilestoneModifyRequest(String name, String description, LocalDateTime dueDate) { + this.name = name; + this.description = description; + this.dueDate = dueDate; + } +} diff --git a/be/src/main/java/com/issuetrackermax/controller/milestone/dto/request/MilestonePostRequest.java b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/request/MilestonePostRequest.java new file mode 100644 index 000000000..5394d4544 --- /dev/null +++ b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/request/MilestonePostRequest.java @@ -0,0 +1,22 @@ +package com.issuetrackermax.controller.milestone.dto.request; + +import java.time.LocalDateTime; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class MilestonePostRequest { + private String name; + private LocalDateTime dueDate; + private String description; + + @Builder + public MilestonePostRequest(String name, LocalDateTime dueDate, String description) { + this.name = name; + this.dueDate = dueDate; + this.description = description; + } +} diff --git a/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneCloseResponse.java b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneCloseResponse.java new file mode 100644 index 000000000..0e986b536 --- /dev/null +++ b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneCloseResponse.java @@ -0,0 +1,21 @@ +package com.issuetrackermax.controller.milestone.dto.response; + +import java.util.List; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class MilestoneCloseResponse { + private Long labelCount; + private Long openMilestoneCount; + private List milestones; + + @Builder + public MilestoneCloseResponse(Long labelCount, Long openMilestoneCount, + List milestones) { + this.labelCount = labelCount; + this.openMilestoneCount = openMilestoneCount; + this.milestones = milestones; + } +} diff --git a/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneDetailResponse.java b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneDetailResponse.java new file mode 100644 index 000000000..ca3e094fb --- /dev/null +++ b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneDetailResponse.java @@ -0,0 +1,27 @@ +package com.issuetrackermax.controller.milestone.dto.response; + +import java.time.LocalDateTime; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class MilestoneDetailResponse { + private Long id; + private String name; + private String description; + private LocalDateTime dueDate; + private Long openIssueCount; + private Long closedIssueCount; + + @Builder + public MilestoneDetailResponse(Long id, String name, String description, LocalDateTime dueDate, + Long openIssueCount, Long closedIssueCount) { + this.id = id; + this.name = name; + this.description = description; + this.dueDate = dueDate; + this.openIssueCount = openIssueCount; + this.closedIssueCount = closedIssueCount; + } +} diff --git a/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneOpenResponse.java b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneOpenResponse.java new file mode 100644 index 000000000..ca13f7435 --- /dev/null +++ b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestoneOpenResponse.java @@ -0,0 +1,23 @@ +package com.issuetrackermax.controller.milestone.dto.response; + +import java.util.List; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class MilestoneOpenResponse { + private Long labelCount; + private Long closedMilestoneCount; + private List milestones; + + @Builder + public MilestoneOpenResponse(Long labelCount, Long closedMilestoneCount, + List milestones) { + this.labelCount = labelCount; + this.closedMilestoneCount = closedMilestoneCount; + this.milestones = milestones; + } +} diff --git a/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestonePostResponse.java b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestonePostResponse.java new file mode 100644 index 000000000..a6257fd6e --- /dev/null +++ b/be/src/main/java/com/issuetrackermax/controller/milestone/dto/response/MilestonePostResponse.java @@ -0,0 +1,14 @@ +package com.issuetrackermax.controller.milestone.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class MilestonePostResponse { + private Long id; + + @Builder + public MilestonePostResponse(Long id) { + this.id = id; + } +} diff --git a/be/src/main/java/com/issuetrackermax/domain/issue/IssueRepository.java b/be/src/main/java/com/issuetrackermax/domain/issue/IssueRepository.java index b88516b63..bca51dae1 100644 --- a/be/src/main/java/com/issuetrackermax/domain/issue/IssueRepository.java +++ b/be/src/main/java/com/issuetrackermax/domain/issue/IssueRepository.java @@ -45,6 +45,11 @@ public IssueResultVO findIssueDetailsById(Long id) { .orElseThrow(() -> new ApiException(IssueException.NOT_FOUND_ISSUE)); } + public List findByMilestoneId(Long milestoneId) { + String sql = "SELECT id, title, is_open, writer_id, milestone_id,created_at FROM issue WHERE milestone_id = :milestoneId"; + return jdbcTemplate.query(sql, Map.of("milestoneId", milestoneId), ISSUE_ROW_MAPPER); + } + public Long save(Issue issue) { String sql = "INSERT INTO issue(title, is_open, writer_id, milestone_id) " + "VALUES (:title, :isOpen, :writerId, :milestoneId)"; diff --git a/be/src/main/java/com/issuetrackermax/domain/milestone/MilestoneRepository.java b/be/src/main/java/com/issuetrackermax/domain/milestone/MilestoneRepository.java index 425ddd05b..28a5c00ab 100644 --- a/be/src/main/java/com/issuetrackermax/domain/milestone/MilestoneRepository.java +++ b/be/src/main/java/com/issuetrackermax/domain/milestone/MilestoneRepository.java @@ -1,10 +1,14 @@ package com.issuetrackermax.domain.milestone; import java.sql.Types; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; @@ -22,28 +26,76 @@ public MilestoneRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); } + public Milestone findbyId(Long id) { + String sql = "SELECT id, title, is_open, duedate, description FROM milestone WHERE id = :id "; + return Optional.ofNullable( + DataAccessUtils.singleResult(jdbcTemplate.query(sql, Map.of("id", id), MILESTONE_ROW_MAPPER))).get(); + } + public Long save(Milestone milestone) { - String sql = "INSERT INTO milestone(title, description,is_open) VALUES (:title,:description,:isOpen)"; + String sql = "INSERT INTO milestone(title, description,is_open, duedate) VALUES (:title,:description,:isOpen, :dueDate)"; KeyHolder keyHolder = new GeneratedKeyHolder(); SqlParameterSource parameters = new MapSqlParameterSource() .addValue("title", milestone.getTitle(), Types.VARCHAR) .addValue("description", milestone.getDescription(), Types.VARCHAR) - .addValue("isOpen", milestone.getIsOpen(), Types.TINYINT); + .addValue("isOpen", milestone.getIsOpen(), Types.TINYINT) + .addValue("dueDate", milestone.getDuedate(), Types.DATE); jdbcTemplate.update(sql, parameters, keyHolder); Map keys = keyHolder.getKeys(); return (Long)Objects.requireNonNull(keys).get("ID"); } + public Long update(Long id, Milestone milestone) { + String sql = "UPDATE milestone SET title = :title, description = :description, duedate = :dueDate WHERE id = :milestoneId"; + KeyHolder keyHolder = new GeneratedKeyHolder(); + SqlParameterSource parameters = new MapSqlParameterSource() + .addValue("milestoneId", id) + .addValue("title", milestone.getTitle(), Types.VARCHAR) + .addValue("description", milestone.getDescription(), Types.VARCHAR) + .addValue("dueDate", milestone.getDuedate(), Types.DATE); + jdbcTemplate.update(sql, parameters, keyHolder); + Map keys = keyHolder.getKeys(); + return (Long)Objects.requireNonNull(keys).get("ID"); + } + public Long getMilestoneCount() { String sql = "SELECT COUNT(*) FROM milestone"; return jdbcTemplate.queryForObject(sql, new MapSqlParameterSource(), Long.class); } + public List getOpenMilestone() { + String sql = "SELECT id, title, is_open, description,duedate FROM milestone WHERE is_open = :isOpen"; + return jdbcTemplate.query(sql, Map.of("isOpen", 1), MILESTONE_ROW_MAPPER); + } + + public List getClosedMilestone() { + String sql = "SELECT id, title, is_open, description,duedate FROM milestone WHERE is_open = :isOpen"; + return jdbcTemplate.query(sql, Map.of("isOpen", 0), MILESTONE_ROW_MAPPER); + } + public Boolean existById(Long id) { String sql = "SELECT EXISTS (SELECT 1 FROM milestone WHERE id = :id)"; return jdbcTemplate.queryForObject(sql, new MapSqlParameterSource() .addValue("id", id), Boolean.class); } + public int deleteById(Long id) { + String sql = "DELETE FROM milestone WHERE id = :id"; + return jdbcTemplate.update(sql, new MapSqlParameterSource("id", id)); + } + + private static final RowMapper MILESTONE_ROW_MAPPER = (rs, rowNum) -> + Milestone.builder() + .id(rs.getLong("id")) + .title(rs.getString("title")) + .isOpen(rs.getBoolean("is_open")) + .description(rs.getString("description")) + .duedate(rs.getTimestamp("duedate").toLocalDateTime()) + .build(); + + public int updateStatus(Long id) { + String sql = "UPDATE milestone SET is_open = NOT is_open WHERE id = :id"; + return jdbcTemplate.update(sql, new MapSqlParameterSource("id", id)); + } } diff --git a/be/src/main/java/com/issuetrackermax/domain/milestone/entity/Milestone.java b/be/src/main/java/com/issuetrackermax/domain/milestone/entity/Milestone.java index 6542fff2d..374656b7b 100644 --- a/be/src/main/java/com/issuetrackermax/domain/milestone/entity/Milestone.java +++ b/be/src/main/java/com/issuetrackermax/domain/milestone/entity/Milestone.java @@ -2,6 +2,9 @@ import java.time.LocalDateTime; +import com.issuetrackermax.controller.milestone.dto.request.MilestoneModifyRequest; +import com.issuetrackermax.controller.milestone.dto.request.MilestonePostRequest; + import lombok.Builder; import lombok.Getter; @@ -21,4 +24,24 @@ public Milestone(Long id, String title, String description, LocalDateTime duedat this.duedate = duedate; this.isOpen = isOpen; } + + public static Milestone from(MilestonePostRequest milestonePostRequest) { + return Milestone + .builder() + .title(milestonePostRequest.getName()) + .description(milestonePostRequest.getDescription()) + .duedate(milestonePostRequest.getDueDate()) + .isOpen(true) + .build(); + } + + public static Milestone from(MilestoneModifyRequest milestoneModifyRequest) { + return Milestone + .builder() + .title(milestoneModifyRequest.getName()) + .description(milestoneModifyRequest.getDescription()) + .duedate(milestoneModifyRequest.getDueDate()) + .build(); + + } } diff --git a/be/src/main/java/com/issuetrackermax/service/milestone/MilestoneService.java b/be/src/main/java/com/issuetrackermax/service/milestone/MilestoneService.java index c4b3d74ce..edfaf1b1e 100644 --- a/be/src/main/java/com/issuetrackermax/service/milestone/MilestoneService.java +++ b/be/src/main/java/com/issuetrackermax/service/milestone/MilestoneService.java @@ -1,8 +1,17 @@ package com.issuetrackermax.service.milestone; +import java.util.List; +import java.util.stream.Collectors; + import org.springframework.stereotype.Service; +import com.issuetrackermax.controller.milestone.dto.request.MilestoneModifyRequest; +import com.issuetrackermax.controller.milestone.dto.request.MilestonePostRequest; +import com.issuetrackermax.controller.milestone.dto.response.MilestoneDetailResponse; +import com.issuetrackermax.domain.issue.IssueRepository; +import com.issuetrackermax.domain.issue.entity.Issue; import com.issuetrackermax.domain.milestone.MilestoneRepository; +import com.issuetrackermax.domain.milestone.entity.Milestone; import lombok.RequiredArgsConstructor; @@ -10,9 +19,68 @@ @Service public class MilestoneService { private final MilestoneRepository milestoneRepository; + private final IssueRepository issueRepository; - public Long getMilstoneCount() { + public Long getMilestoneCount() { return milestoneRepository.getMilestoneCount(); } + public List getOpenMilestone() { + List openMilestone = milestoneRepository.getOpenMilestone(); + return getMilestoneDetailResponses(openMilestone); + } + + public List getClosedMilestone() { + List closedMilestone = milestoneRepository.getClosedMilestone(); + return getMilestoneDetailResponses(closedMilestone); + + } + + private List getMilestoneDetailResponses(List openMilestone) { + List response = + openMilestone.stream() + .map(milestone -> { + long openIssueCount = issueRepository.findByMilestoneId(milestone.getId()) + .stream() + .filter(Issue::getIsOpen) + .count(); + + long closedIssueCount = issueRepository.findByMilestoneId(milestone.getId()) + .stream() + .filter(issue -> !issue.getIsOpen()) + .count(); + + return MilestoneDetailResponse.builder() + .id(milestone.getId()) + .description(milestone.getDescription()) + .name(milestone.getTitle()) + .dueDate(milestone.getDuedate()) + .openIssueCount(openIssueCount) + .closedIssueCount(closedIssueCount) + .build(); + }) + .collect(Collectors.toList()); + + return response; + } + + public Long save(MilestonePostRequest milestonePostRequest) { + Milestone milestone = Milestone.from(milestonePostRequest); + return milestoneRepository.save(milestone); + } + + public void update(Long id, MilestoneModifyRequest milestoneModifyRequest) { + milestoneRepository.update(id, Milestone.from(milestoneModifyRequest)); + return; + } + + public void delete(Long id) { + int count = milestoneRepository.deleteById(id); + return; + } + + public void updateStatus(Long id) { + milestoneRepository.updateStatus(id); + return; + } }