From a15fc6f5e4c5f5b56e5080cfd8b9d18d02ba7722 Mon Sep 17 00:00:00 2001 From: "DESKTOP-N1D5NOF\\User" Date: Mon, 14 Jan 2019 02:01:12 +0900 Subject: [PATCH 1/2] =?UTF-8?q?ajax=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EB=A7=88=EC=9D=BC=EC=8A=A4=ED=86=A4,=20=EB=9D=BC?= =?UTF-8?q?=EB=B2=A8,=20=EC=9D=B4=EC=8A=88=20=EB=8B=B4=EB=8B=B9=EC=9E=90?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=9D=84=EC=9A=B0=EA=B8=B0?= =?UTF-8?q?=EC=99=80=20=EC=A7=80=EC=A0=95=ED=95=98=EA=B8=B0=EB=A5=BC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=ED=96=88=EC=9C=BC=EB=A9=B0,=20=EC=9D=B4?= =?UTF-8?q?=EC=8A=88=EC=9D=98=20=EB=8B=B5=EA=B8=80=EB=8B=AC=EA=B8=B0=20api?= =?UTF-8?q?=EB=8F=84=20=EB=A7=8C=EB=93=A4=EC=96=B4=20ajax=EB=A5=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=B4,=20=EC=B6=94=EA=B0=80=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=ED=95=A0=EC=88=98=20=EC=9E=88=EA=B2=8C=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A4=EC=97=88=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/codesquad/WebMvcConfig.java | 20 ++ src/main/java/codesquad/domain/Answer.java | 99 ++++++ .../codesquad/domain/AnswerRepository.java | 12 + .../java/codesquad/domain/ContentType.java | 2 +- src/main/java/codesquad/domain/Issue.java | 32 +- src/main/java/codesquad/domain/Label.java | 27 ++ src/main/java/codesquad/domain/Milestone.java | 28 +- .../java/codesquad/service/AnswerService.java | 47 +++ .../java/codesquad/service/LabelService.java | 27 +- .../codesquad/service/MilestoneService.java | 20 +- .../java/codesquad/service/UserService.java | 11 + .../java/codesquad/web/AnswerController.java | 44 +++ .../codesquad/web/ApiAnswerController.java | 59 ++++ .../codesquad/web/ApiIssueController.java | 18 +- .../codesquad/web/ApiLabelController.java | 52 +++ .../codesquad/web/ApiMilestoneController.java | 22 +- .../java/codesquad/web/IssueController.java | 17 +- .../codesquad/web/MilestoneController.java | 2 +- src/main/resources/application.properties | 3 +- src/main/resources/import.sql | 10 +- src/main/resources/static/js/comment.js | 75 ++++ src/main/resources/static/js/main.js | 188 +++++++++- src/main/resources/templates/base.html | 1 + src/main/resources/templates/index.html | 25 -- src/main/resources/templates/issue/show.html | 333 ++++++++---------- .../java/codesquad/domain/MilestoneTest.java | 4 +- .../codesquad/web/ApiLabelAcceptanceTest.java | 21 ++ 27 files changed, 957 insertions(+), 242 deletions(-) create mode 100644 src/main/java/codesquad/domain/Answer.java create mode 100644 src/main/java/codesquad/domain/AnswerRepository.java create mode 100644 src/main/java/codesquad/service/AnswerService.java create mode 100644 src/main/java/codesquad/web/AnswerController.java create mode 100644 src/main/java/codesquad/web/ApiAnswerController.java create mode 100644 src/main/java/codesquad/web/ApiLabelController.java create mode 100644 src/main/resources/static/js/comment.js create mode 100644 src/test/java/codesquad/web/ApiLabelAcceptanceTest.java diff --git a/src/main/java/codesquad/WebMvcConfig.java b/src/main/java/codesquad/WebMvcConfig.java index 2bb13656..e5082a2a 100644 --- a/src/main/java/codesquad/WebMvcConfig.java +++ b/src/main/java/codesquad/WebMvcConfig.java @@ -8,12 +8,17 @@ import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.format.FormatterRegistry; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import support.converter.LocalDateConverter; import support.converter.LocalDateTimeConverter; +import javax.servlet.Filter; +import java.nio.charset.Charset; import java.util.List; @Configuration @@ -53,6 +58,21 @@ public LoginUserHandlerMethodArgumentResolver loginUserArgumentResolver() { return new LoginUserHandlerMethodArgumentResolver(); } + + + @Bean + public Filter characterEncodingFilter() { + CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); + characterEncodingFilter.setEncoding("UTF-8"); + characterEncodingFilter.setForceEncoding(true); + return characterEncodingFilter; + } + + @Bean + public HttpMessageConverter responseBodyConverter() { + return new StringHttpMessageConverter(Charset.forName("UTF-8")); + } + @Override public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(loginUserArgumentResolver()); diff --git a/src/main/java/codesquad/domain/Answer.java b/src/main/java/codesquad/domain/Answer.java new file mode 100644 index 00000000..9144eb9f --- /dev/null +++ b/src/main/java/codesquad/domain/Answer.java @@ -0,0 +1,99 @@ +package codesquad.domain; + +import codesquad.CannotDeleteException; +import codesquad.UnAuthorizedException; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.slf4j.Logger; +import support.domain.AbstractEntity; + +import javax.persistence.*; +import javax.validation.constraints.Size; + +import java.util.ArrayList; +import java.util.List; + +import static org.slf4j.LoggerFactory.getLogger; + +@Entity +public class Answer extends AbstractEntity { + private static final Logger log = getLogger(Answer.class); + + @Size(min = 2, max = 500) + @Column(nullable = false, length = 500) + private String comment; + + @ManyToOne + @JoinColumn(foreignKey = @ForeignKey(name = "fk_comment_writer")) + private User writer; + + @JsonIgnore + @ManyToOne + @JoinColumn(foreignKey = @ForeignKey(name = "fk_comment_issue")) + private Issue issue; + + private boolean deleted = false; + + public Answer() { + } + + public Answer(User loginUser, Issue issue, String newComment) { + this.writer = loginUser; + this.issue = issue; + this.comment = newComment; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public User getWriter() { + return writer; + } + + public void setWriter(User writer) { + this.writer = writer; + } + + public Issue getIssue() { + return issue; + } + + public void setIssue(Issue issue) { + this.issue = issue; + } + + public boolean isDeleted() { + return deleted; + } + + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } + + public void update(String update, User loginUser) { + if (!this.writer.equals(loginUser)) { + throw new UnAuthorizedException(); + } + this.comment = update; + } + + public DeleteHistory delete(User loginUser) { + if (!this.writer.equals(loginUser)) { + throw new CannotDeleteException("다른사람의 답글을 지울수 없습니다."); + } + this.deleted = true; + return new DeleteHistory(ContentType.ANSWER, getId(), loginUser); + } + + public static List deleteAnswers(List answers, User loginUser) { + List answersInfo = new ArrayList<>(); + for (Answer answer : answers) { + answersInfo.add(answer.delete(loginUser)); + } + return answersInfo; + } +} diff --git a/src/main/java/codesquad/domain/AnswerRepository.java b/src/main/java/codesquad/domain/AnswerRepository.java new file mode 100644 index 00000000..7fd6cb5a --- /dev/null +++ b/src/main/java/codesquad/domain/AnswerRepository.java @@ -0,0 +1,12 @@ +package codesquad.domain; + +import org.springframework.data.repository.CrudRepository; + +import java.util.List; + +public interface AnswerRepository extends CrudRepository { + + Iterable findByDeleted(boolean b); + + List findByIssueId(long id); +} diff --git a/src/main/java/codesquad/domain/ContentType.java b/src/main/java/codesquad/domain/ContentType.java index bb40211d..38d207e1 100644 --- a/src/main/java/codesquad/domain/ContentType.java +++ b/src/main/java/codesquad/domain/ContentType.java @@ -1,5 +1,5 @@ package codesquad.domain; public enum ContentType { - ISSUE + ISSUE, ANSWER } diff --git a/src/main/java/codesquad/domain/Issue.java b/src/main/java/codesquad/domain/Issue.java index bea949d5..f46144d1 100644 --- a/src/main/java/codesquad/domain/Issue.java +++ b/src/main/java/codesquad/domain/Issue.java @@ -3,6 +3,7 @@ import codesquad.CannotDeleteException; import codesquad.UnAuthorizedException; import codesquad.dto.IssueDto; +import org.hibernate.annotations.Where; import support.domain.AbstractEntity; import javax.persistence.*; @@ -33,6 +34,14 @@ public class Issue extends AbstractEntity { @JoinColumn(foreignKey = @ForeignKey(name = "fk_issue_label")) private Label label; + @ManyToOne + @JoinColumn(foreignKey = @ForeignKey(name = "fk_issue_assignee")) + private User assignee; + + @OneToMany + @Where(clause = "deleted = false") + @OrderBy("id ASC") + private List answers = new ArrayList<>(); private boolean deleted = false; @@ -78,6 +87,22 @@ public void setComment(String comment) { this.comment = comment; } + public User getAssignee() { + return assignee; + } + + public void setAssignee(User assignee) { + this.assignee = assignee; + } + + public List getComments() { + return answers; + } + + public void setComments(List comments) { + this.answers = comments; + } + public User getWriter() { return writer; } @@ -144,11 +169,16 @@ public List delete(User loginUser) { throw new CannotDeleteException("작성자가 아니면 지울수 없습니다."); } this.deleted = true; - List deletes = new ArrayList<>(); + List deletes = Answer.deleteAnswers(this.answers, loginUser); deletes.add(new DeleteHistory(ContentType.ISSUE, getId(), writer)); return deletes; } + + public void addComment(Answer newComment) { + answers.add(newComment); + } + public boolean equalsQuestion(Issue issue) { return this.subject.equals(issue.subject) && this.comment.equals(issue.comment); } diff --git a/src/main/java/codesquad/domain/Label.java b/src/main/java/codesquad/domain/Label.java index b3c15ba8..893082f4 100644 --- a/src/main/java/codesquad/domain/Label.java +++ b/src/main/java/codesquad/domain/Label.java @@ -1,5 +1,6 @@ package codesquad.domain; +import com.fasterxml.jackson.annotation.JsonIgnore; import support.domain.AbstractEntity; import javax.persistence.*; @@ -11,10 +12,24 @@ public class Label extends AbstractEntity { public Label() { } + public Label(String name) { + this.name = name; + } + + public Label(long id, String name) { + super(id); + this.name = name; + } + @OneToMany(mappedBy = "label") @OrderBy("id ASC") + @JsonIgnore private List issue; + @ManyToOne + @JoinColumn(foreignKey = @ForeignKey(name = "fk_label_writer")) + private User writer; + private String name; public List getIssue() { @@ -32,4 +47,16 @@ public String getName() { public void setName(String name) { this.name = name; } + + public User getWriter() { + return writer; + } + + public void setWriter(User writer) { + this.writer = writer; + } + + public void writeBy(User user) { + this.writer = user; + } } diff --git a/src/main/java/codesquad/domain/Milestone.java b/src/main/java/codesquad/domain/Milestone.java index b24a5218..679460c4 100644 --- a/src/main/java/codesquad/domain/Milestone.java +++ b/src/main/java/codesquad/domain/Milestone.java @@ -1,5 +1,6 @@ package codesquad.domain; +import com.fasterxml.jackson.annotation.JsonIgnore; import org.hibernate.annotations.Where; import support.domain.AbstractEntity; @@ -22,11 +23,12 @@ public class Milestone extends AbstractEntity { @OneToMany(mappedBy = "milestone", cascade = CascadeType.ALL) @Where(clause = "delete = false") @OrderBy("id ASC") + @JsonIgnore private List issues; - private LocalDateTime startDate; + private String startDate; - private LocalDateTime endDate; + private String endDate; public Milestone() { } @@ -35,14 +37,14 @@ public Milestone(String subject) { this.subject = subject; } - public Milestone(String subject, LocalDateTime startDate, LocalDateTime endDate, User user) { + public Milestone(String subject, String startDate, String endDate, User user) { this.subject = subject; this.startDate = startDate; this.endDate = endDate; this.writer = user; } - public Milestone(long id, String subject, LocalDateTime startDate, LocalDateTime endDate, User writer) { + public Milestone(long id, String subject, String startDate, String endDate, User writer) { super(id); this.subject = subject; this.writer = writer; @@ -74,19 +76,19 @@ public void setWriter(User writer) { this.writer = writer; } - public LocalDateTime getStartDate() { + public String getStartDate() { return startDate; } - public void setStartDate(LocalDateTime startDate) { + public void setStartDate(String startDate) { this.startDate = startDate; } - public LocalDateTime getEndDate() { + public String getEndDate() { return endDate; } - public void setEndDate(LocalDateTime endDate) { + public void setEndDate(String endDate) { this.endDate = endDate; } @@ -117,6 +119,16 @@ public int hashCode() { result = 31 * result + (endDate != null ? endDate.hashCode() : 0); return result; } + + @Override + public String toString() { + return "Milestone{" + + "subject='" + subject + '\'' + + ", writer=" + writer + + ", startDate='" + startDate + '\'' + + ", endDate='" + endDate + '\'' + + '}'; + } } diff --git a/src/main/java/codesquad/service/AnswerService.java b/src/main/java/codesquad/service/AnswerService.java new file mode 100644 index 00000000..ef105b14 --- /dev/null +++ b/src/main/java/codesquad/service/AnswerService.java @@ -0,0 +1,47 @@ +package codesquad.service; + +import codesquad.domain.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +@Service +public class AnswerService { + + @Resource(name = "answerRepository") + private AnswerRepository answerRepository; + + @Transactional + public Answer create(User loginUser, Issue issue, @Valid String newComment) { + Answer comment = new Answer(loginUser, issue, newComment); + issue.addComment(comment); + return answerRepository.save(comment); + } + + @Transactional + public Answer update(User loginUser, long id, @Valid String update) { + Answer comment = answerRepository.findById(id).orElseThrow(UnknownError::new); + comment.update(update, loginUser); + return answerRepository.save(comment); + } + + public Iterable findAll() { + return answerRepository.findByDeleted(false); + } + + public List findByIssueId(long id) { + return answerRepository.findByIssueId(id); + } + + public DeleteHistory delete(User loginUser, long id) { + Answer answer = answerRepository.findById(id).orElseThrow(UnknownError::new); + answer.delete(loginUser); + answerRepository.save(answer); + return answer.delete(loginUser); + } + + +} diff --git a/src/main/java/codesquad/service/LabelService.java b/src/main/java/codesquad/service/LabelService.java index 4037287c..cb03a5f6 100644 --- a/src/main/java/codesquad/service/LabelService.java +++ b/src/main/java/codesquad/service/LabelService.java @@ -1,21 +1,42 @@ package codesquad.service; -import codesquad.domain.Label; -import codesquad.domain.LabelRepository; +import codesquad.domain.*; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.net.URI; +import java.util.List; @Service public class LabelService { @Resource(name = "labelRepository") private LabelRepository labelRepository; + @Resource(name = "issueRepository") + private IssueRepository issueRepository; + public void add(Label label) { labelRepository.save(label); } - public Iterable