Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…votogether into feat/#55
  • Loading branch information
inyeong-kang committed Jul 19, 2023
2 parents 34d94bf + c45bce4 commit 310d88b
Show file tree
Hide file tree
Showing 60 changed files with 1,906 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.votogether.domain.category.contorller;

import com.votogether.domain.category.dto.response.CategoryResponse;
import com.votogether.domain.category.service.CategoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Tag(name = "카테고리", description = "카테고리 API")
@RequestMapping("/categories")
@RequiredArgsConstructor
public class CategoryController {

private final CategoryService categoryService;

@Operation(summary = "카테고리 조회하기", description = "전체 카테고리 목록을 조회한다.")
@ApiResponse(responseCode = "200", description = "조회 성공")
@GetMapping("/guest")
public ResponseEntity<List<CategoryResponse>> getAllCategories() {
List<CategoryResponse> categories = categoryService.getAllCategories();
return ResponseEntity.status(HttpStatus.OK).body(categories);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.votogether.domain.category.dto.response;

import com.votogether.domain.category.entity.Category;

public record CategoryResponse(
Long id,
String name,
boolean isFavorite
) {

public CategoryResponse(final Category category) {
this(category.getId(), category.getName(), false);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = {"name"})
@Getter
@Entity
public class Category extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(length = 50, nullable = false)
@Column(length = 50, unique = true, nullable = false)
private String name;

@Builder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.votogether.domain.category.service;

import com.votogether.domain.category.dto.response.CategoryResponse;
import com.votogether.domain.category.entity.Category;
import com.votogether.domain.category.repository.CategoryRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class CategoryService {

private final CategoryRepository categoryRepository;

@Transactional(readOnly = true)
public List<CategoryResponse> getAllCategories() {
List<Category> categories = categoryRepository.findAll();

return categories.stream()
.map(CategoryResponse::new)
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@ private Member(
this.point = point;
}

public void plusPoint(final int point) {
this.point = this.point + point;
}

}
48 changes: 48 additions & 0 deletions backend/src/main/java/com/votogether/domain/post/entity/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.votogether.domain.common.BaseEntity;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.vote.entity.Vote;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand All @@ -10,7 +11,10 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -38,6 +42,9 @@ public class Post extends BaseEntity {
@Column(columnDefinition = "datetime(2)", nullable = false)
private LocalDateTime deadline;

@OneToMany(mappedBy = "post")
private List<PostOption> postOptions = new ArrayList<>();

@Builder
private Post(
final Member member,
Expand All @@ -51,4 +58,45 @@ private Post(
this.deadline = deadline;
}

public boolean hasPostOption(final PostOption postOption) {
return postOptions.contains(postOption);
}

public boolean isWriter(final Member member) {
return this.member == member;
}

public boolean isClosed() {
return deadline.isBefore(LocalDateTime.now());
}

public Vote makeVote(Member member, PostOption postOption) {
validateDeadLine();
validateWriter(member);
validatePostOption(postOption);

return Vote.builder()
.member(member)
.postOption(postOption)
.build();
}

private void validateDeadLine() {
if (isClosed()) {
throw new IllegalStateException("게시글이 이미 마감되었습니다.");
}
}

private void validateWriter(Member member) {
if (isWriter(member)) {
throw new IllegalArgumentException("작성자는 투표할 수 없습니다.");
}
}

private void validatePostOption(PostOption postOption) {
if (!hasPostOption(postOption)) {
throw new IllegalArgumentException("해당 게시글에서 존재하지 않는 선택지 입니다.");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.votogether.domain.post.repository;

import com.votogether.domain.post.entity.PostOption;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PostOptionRepository extends JpaRepository<PostOption, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.votogether.domain.vote.controller;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.vote.service.VoteService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Tag(name = "투표", description = "투표 API")
@RequiredArgsConstructor
public class VoteController {

private final VoteService voteService;

@Operation(summary = "투표하기", description = "게시글의 선택지에 투표를 한다.")
@ApiResponse(responseCode = "201", description = "투표 성공")
@PostMapping("/posts/{postId}/options/{optionId}")
public ResponseEntity<Void> vote(
@PathVariable final Long postId,
@PathVariable("optionId") final Long postOptionId,
final Member member
) {
voteService.vote(member, postId, postOptionId);
return ResponseEntity.status(HttpStatus.CREATED).build();
}


@Operation(summary = "투표 수정하기", description = "게시글의 선택지의 투표를 수정한다.")
@ApiResponse(responseCode = "200", description = "투표 수정 성공")
@PatchMapping("/posts/{postId}/options")
public ResponseEntity<Void> changeVote(
@PathVariable final Long postId,
@RequestParam("source") final Long originPostOptionId,
@RequestParam("target") final Long newPostOptionId,
final Member member
) {
voteService.changeVote(member, postId, originPostOptionId, newPostOptionId);
return ResponseEntity.status(HttpStatus.OK).build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class Vote extends BaseEntity {

@Id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.votogether.domain.vote.repository;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.post.entity.PostOption;
import com.votogether.domain.vote.entity.Vote;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface VoteRepository extends JpaRepository<Vote, Long> {

Optional<Vote> findByMemberAndPostOption(final Member member, final PostOption postOption);

List<Vote> findByMemberAndPostOptionIn(final Member member, final List<PostOption> postOptions);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.votogether.domain.vote.service;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.post.entity.Post;
import com.votogether.domain.post.entity.PostOption;
import com.votogether.domain.post.repository.PostOptionRepository;
import com.votogether.domain.post.repository.PostRepository;
import com.votogether.domain.vote.entity.Vote;
import com.votogether.domain.vote.repository.VoteRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class VoteService {

private final VoteRepository voteRepository;
private final PostRepository postRepository;
private final PostOptionRepository postOptionRepository;

public void vote(
final Member member,
final Long postId,
final Long postOptionId
) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다."));

validateAlreadyVoted(member, post);

PostOption postOption = postOptionRepository.findById(postOptionId)
.orElseThrow(() -> new IllegalArgumentException("해당 선택지가 존재하지 않습니다."));

Vote vote = post.makeVote(member, postOption);
member.plusPoint(1);
voteRepository.save(vote);
}

private void validateAlreadyVoted(Member member, Post post) {
List<Vote> alreadyVoted = voteRepository.findByMemberAndPostOptionIn(member, post.getPostOptions());
if (!alreadyVoted.isEmpty()) {
throw new IllegalStateException("해당 게시물에는 이미 투표하였습니다.");
}
}

public void changeVote(
final Member member,
final Long postId,
final Long originPostOptionId,
final Long newPostOptionId
) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다."));

PostOption originPostOption = postOptionRepository.findById(originPostOptionId)
.orElseThrow(() -> new IllegalArgumentException("헤당 선택지가 존재하지 않습니다."));

Vote originVote = voteRepository.findByMemberAndPostOption(member, originPostOption)
.orElseThrow(() -> new IllegalArgumentException("선택지에 해당되는 투표가 존재하지 않습니다."));

PostOption newPostOption = postOptionRepository.findById(newPostOptionId)
.orElseThrow(() -> new IllegalArgumentException("헤당 선택지가 존재하지 않습니다."));

voteRepository.delete(originVote);
Vote vote = post.makeVote(member, newPostOption);
voteRepository.save(vote);
}

}
18 changes: 18 additions & 0 deletions backend/src/test/java/com/votogether/domain/RepositoryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.votogether.domain;

import com.votogether.config.JpaConfig;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(JpaConfig.class)
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface RepositoryTest {
}
Loading

0 comments on commit 310d88b

Please sign in to comment.