Skip to content

Commit

Permalink
Merge branch 'dev' into feat/#55
Browse files Browse the repository at this point in the history
  • Loading branch information
inyeong-kang authored Jul 19, 2023
2 parents 310d88b + 3a07b70 commit 32c82ef
Show file tree
Hide file tree
Showing 46 changed files with 1,213 additions and 70 deletions.
3 changes: 3 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'

testImplementation 'io.rest-assured:rest-assured'
testImplementation 'io.rest-assured:spring-mock-mvc'

runtimeOnly 'com.h2database:h2'

compileOnly 'org.projectlombok:lombok'
Expand Down
18 changes: 18 additions & 0 deletions backend/src/main/java/com/votogether/config/SwaggerBeanConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.votogether.config;

import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

@Configuration
public class SwaggerBeanConfig {

public SwaggerBeanConfig(final MappingJackson2HttpMessageConverter converter) {
final List<MediaType> supportedMediaTypes = new ArrayList<>(converter.getSupportedMediaTypes());
supportedMediaTypes.add(new MediaType("application", "octet-stream"));
converter.setSupportedMediaTypes(supportedMediaTypes);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.votogether.domain.category.dto.response.CategoryResponse;
import com.votogether.domain.category.service.CategoryService;
import com.votogether.domain.member.entity.Member;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -10,6 +11,8 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -29,4 +32,12 @@ public ResponseEntity<List<CategoryResponse>> getAllCategories() {
return ResponseEntity.status(HttpStatus.OK).body(categories);
}

@Operation(summary = "선호 카테고리 추가하기", description = "선호하는 카테고리를 선호 카테고리 목록에 추가한다.")
@ApiResponse(responseCode = "201", description = "추가 성공")
@PostMapping("/{categoryId}/like")
public ResponseEntity<Void> addFavoriteCategory(final Member member, @PathVariable final Long categoryId) {
categoryService.addFavoriteCategory(member, categoryId);
return ResponseEntity.status(HttpStatus.CREATED).build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import com.votogether.domain.category.dto.response.CategoryResponse;
import com.votogether.domain.category.entity.Category;
import com.votogether.domain.category.repository.CategoryRepository;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.MemberCategory;
import com.votogether.domain.member.repository.MemberCategoryRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -13,6 +16,7 @@
public class CategoryService {

private final CategoryRepository categoryRepository;
private final MemberCategoryRepository memberCategoryRepository;

@Transactional(readOnly = true)
public List<CategoryResponse> getAllCategories() {
Expand All @@ -23,4 +27,20 @@ public List<CategoryResponse> getAllCategories() {
.toList();
}

@Transactional
public void addFavoriteCategory(final Member member, final Long categoryId) {
Category category = categoryRepository.findById(categoryId)
.orElseThrow(() -> new IllegalArgumentException("해당 카테고리가 존재하지 않습니다."));

memberCategoryRepository.findByMemberAndCategory(member, category)
.ifPresent(ignore -> new IllegalStateException("이미 선호 카테고리에 등록되어 있습니다."));

MemberCategory memberCategory = MemberCategory.builder()
.member(member)
.category(category)
.build();

memberCategoryRepository.save(memberCategory);
}

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

import com.votogether.domain.category.entity.Category;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.MemberCategory;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberCategoryRepository extends JpaRepository<MemberCategory, Long> {
Optional<MemberCategory> findByMemberAndCategory(final Member member, final Category category);

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

import com.votogether.domain.member.entity.Gender;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.SocialType;
import com.votogether.domain.post.dto.request.PostCreateRequest;
import com.votogether.domain.post.service.PostService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.net.URI;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Tag(name = "게시글", description = "게시글 관련 API")
@RequiredArgsConstructor
@RequestMapping("/posts")
@RestController
public class PostController {

private final PostService postService;

@Operation(summary = "게시글 작성", description = "게시글을 저장한다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "게시물 생성되었습니다."),
@ApiResponse(responseCode = "400", description = "잘못된 입력입니다."),
@ApiResponse(responseCode = "500", description = "인터넷 서버 오류입니다.")
})
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> save(
@RequestPart final PostCreateRequest request,
@RequestPart final List<MultipartFile> images
) {
// TODO : 일단 돌아가게 하기 위한 member 저장 (실제 어플에선 삭제될 코드)
final Member member = Member.builder()
.socialType(SocialType.GOOGLE)
.socialId("tjdtls690")
.nickname("Abel")
.gender(Gender.MALE)
.point(100)
.birthDate(LocalDateTime.now())
.build();

final Long postId = postService.save(request, member, images);
return ResponseEntity.created(URI.create("/posts/" + postId)).build();
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.votogether.domain.post.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Builder;
import org.springframework.format.annotation.DateTimeFormat;

@Schema(name = "게시글 관련 데이터", description = "게시글에 관련한 데이터들입니다.")
@Builder
public record PostCreateRequest (
List<Long> categoryIds,
String title,
String content,
List<String> postOptionContents,

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime deadline
){

}
57 changes: 44 additions & 13 deletions backend/src/main/java/com/votogether/domain/post/entity/Post.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
package com.votogether.domain.post.entity;

import com.votogether.domain.category.entity.Category;
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.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
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 java.util.stream.IntStream;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
Expand All @@ -33,29 +35,58 @@ public class Post extends BaseEntity {
@JoinColumn(name = "member_id", nullable = false)
private Member member;

@Column(length = 100, nullable = false)
private String title;
@Embedded
private PostBody postBody;

@Column(length = 1000, nullable = false)
private String content;
@Embedded
private PostCategories postCategories;

@Embedded
private PostOptions postOptions;

@Column(columnDefinition = "datetime(2)", nullable = false)
private LocalDateTime deadline;

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

@Builder
private Post(
final Member member,
final String title,
final String content,
final PostBody postBody,
final LocalDateTime deadline
) {
this.member = member;
this.title = title;
this.content = content;
this.postBody = postBody;
this.deadline = deadline;
this.postCategories = new PostCategories();
this.postOptions = new PostOptions();
}

public void mapCategories(final List<Category> categories) {
this.postCategories.mapPostAndCategories(this, categories);
}

public void mapPostOptionsByElements(
final List<String> postOptionContents,
final Post post,
final List<MultipartFile> images
) {
this.postOptions.addAllPostOptions(toPostOptionEntities(postOptionContents, post, images));
}

private List<PostOption> toPostOptionEntities(
final List<String> postOptionContents,
final Post post,
final List<MultipartFile> images
) {
return IntStream.range(0, postOptionContents.size())
.mapToObj(postOptionSequence ->
PostOption.of(
postOptionContents.get(postOptionSequence),
post,
postOptionSequence,
images.get(postOptionSequence)
)
)
.toList();
}

public boolean hasPostOption(final PostOption postOption) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.votogether.domain.post.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Embeddable
public class PostBody {

@Column(length = 100, nullable = false)
private String title;

@Column(length = 1000, nullable = false)
private String content;

@Builder
private PostBody(final String title, final String content) {
this.title = title;
this.content = content;
}

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

import com.votogether.domain.category.entity.Category;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Embeddable;
import jakarta.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Embeddable
public class PostCategories {

@OneToMany(mappedBy = "post", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<PostCategory> postCategories = new ArrayList<>();

public void mapPostAndCategories(final Post post, final List<Category> categories) {
categories.forEach(category -> postCategories.add(createPostCategory(post, category)));
}

private PostCategory createPostCategory(final Post post, final Category category) {
return PostCategory.builder()
.post(post)
.category(category)
.build();
}

}
Loading

0 comments on commit 32c82ef

Please sign in to comment.