Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] 게시물 업로드, 상세 조회을 구현한다 #34

Merged
merged 5 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package daybyquest.global.error.exception;

import daybyquest.global.error.ExceptionCode;

public class NotExistPostException extends CustomException {

public NotExistPostException() {
super(ExceptionCode.NOT_EXIST_POST);
}
}
29 changes: 29 additions & 0 deletions src/main/java/daybyquest/post/application/GetPostService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package daybyquest.post.application;

import daybyquest.post.dto.response.PostResponse;
import daybyquest.post.query.PostDao;
import daybyquest.post.query.PostData;
import daybyquest.user.query.Profile;
import daybyquest.user.query.ProfileDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class GetPostService {

private final PostDao postDao;

private final ProfileDao profileDao;

public GetPostService(final PostDao postDao, final ProfileDao profileDao) {
this.postDao = postDao;
this.profileDao = profileDao;
}

@Transactional(readOnly = true)
public PostResponse invoke(final Long loginId, final Long postId) {
final PostData postData = postDao.getByPostId(loginId, postId);
final Profile profile = profileDao.getById(loginId, postData.getUserId());
return PostResponse.of(postData, profile);
}
}
56 changes: 56 additions & 0 deletions src/main/java/daybyquest/post/application/SavePostService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package daybyquest.post.application;

import daybyquest.global.utils.MultipartFileUtils;
import daybyquest.image.vo.Image;
import daybyquest.image.vo.ImageIdentifierGenerator;
import daybyquest.image.vo.Images;
import daybyquest.post.domain.Post;
import daybyquest.post.domain.Posts;
import daybyquest.post.dto.request.SavePostRequest;
import daybyquest.user.domain.Users;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
public class SavePostService {

private static final String CATEGORY = "POST";

private final Users users;

private final Posts posts;

private final Images images;

private final ImageIdentifierGenerator generator;

public SavePostService(final Users users, final Posts posts, final Images images,
final ImageIdentifierGenerator generator) {
this.users = users;
this.posts = posts;
this.images = images;
this.generator = generator;
}

@Transactional
public Long invoke(final Long loginId, final SavePostRequest request,
final List<MultipartFile> files) {
users.validateExistentById(loginId);
final Post post = toEntity(loginId, request, toImageList(files));
return posts.save(post);
}

private List<Image> toImageList(final List<MultipartFile> files) {
return files.stream().map((file) -> {
final String identifier = generator.generateIdentifier(CATEGORY, file.getOriginalFilename());
images.upload(identifier, MultipartFileUtils.getInputStream(file));
return new Image(identifier);
}).toList();
}

private Post toEntity(final Long loginId, final SavePostRequest request, final List<Image> images) {
return new Post(loginId, request.getQuestId(), request.getContent(), images);
}
}
36 changes: 30 additions & 6 deletions src/main/java/daybyquest/post/domain/Post.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package daybyquest.post.domain;

import static daybyquest.post.domain.PostState.NOT_DECIDED;
import static daybyquest.post.domain.PostState.PREPARING;
import static daybyquest.post.domain.PostState.SUCCESS;
import static jakarta.persistence.GenerationType.IDENTITY;

import daybyquest.global.error.exception.InvalidDomainException;
import daybyquest.image.vo.Image;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
Expand All @@ -28,13 +32,16 @@
@EntityListeners(AuditingEntityListener.class)
public class Post {

private static final int MAX_IMAGE_SIZE = 5;

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

@Column(nullable = false)
private Long userId;

@Column
private Long questId;

@LastModifiedDate
Expand All @@ -51,15 +58,32 @@ public class Post {
@OrderColumn(name = "order")
private List<Image> images;

private boolean deleted;


public Post(Long userId, Long questId, String content, List<Image> images, boolean deleted) {
public Post(Long userId, Long questId, String content, List<Image> images) {
this.userId = userId;
this.questId = questId;
this.content = content;
this.images = images;
this.deleted = deleted;
this.state = PostState.NOT_DECIDED;
this.state = PREPARING;
validateImages();
}

private void validateImages() {
if (this.images.isEmpty() || this.images.size() > MAX_IMAGE_SIZE) {
throw new InvalidDomainException();
}
}

public void afterRequestDeciding() {
if (this.state != PREPARING || this.images == null) {
throw new InvalidDomainException();
}
this.state = NOT_DECIDED;
}

public void success() {
if (this.state != PREPARING && this.state != NOT_DECIDED) {
throw new InvalidDomainException();
}
this.state = SUCCESS;
}
}
2 changes: 1 addition & 1 deletion src/main/java/daybyquest/post/domain/PostRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.util.Optional;
import org.springframework.data.repository.Repository;

public interface PostRepository extends Repository<Post, Long> {
interface PostRepository extends Repository<Post, Long> {

Post save(Post post);

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/daybyquest/post/domain/PostState.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

public enum PostState {

PREPARING,
SUCCESS,
NEED_CHECK,
FAIL,
NOT_DECIDED;
NOT_DECIDED,
DELETED;
}
22 changes: 22 additions & 0 deletions src/main/java/daybyquest/post/domain/Posts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package daybyquest.post.domain;

import daybyquest.global.error.exception.NotExistPostException;
import org.springframework.stereotype.Component;

@Component
public class Posts {

private final PostRepository postRepository;

public Posts(final PostRepository postRepository) {
this.postRepository = postRepository;
}

public Long save(final Post post) {
return postRepository.save(post).getId();
}

public Post getById(final Long id) {
return postRepository.findById(id).orElseThrow(NotExistPostException::new);
}
}
13 changes: 13 additions & 0 deletions src/main/java/daybyquest/post/dto/request/SavePostRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package daybyquest.post.dto.request;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class SavePostRequest {

private String content;

private Long questId;
}
45 changes: 45 additions & 0 deletions src/main/java/daybyquest/post/dto/response/PostResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package daybyquest.post.dto.response;

import daybyquest.image.vo.Image;
import daybyquest.post.query.PostData;
import daybyquest.user.dto.response.ProfileResponse;
import daybyquest.user.query.Profile;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class PostResponse {

private ProfileResponse author;

private Long id;

private String content;

private LocalDateTime updatedAt;

private boolean liked;

private List<String> images;

public PostResponse(final ProfileResponse author, final Long id, final String content,
final LocalDateTime updatedAt, final boolean liked,
final List<String> images) {
this.author = author;
this.id = id;
this.content = content;
this.updatedAt = updatedAt;
this.liked = liked;
this.images = images;
}

public static PostResponse of(final PostData postData, final Profile profile) {
return new PostResponse(ProfileResponse.of(profile), postData.getId(), postData.getContent(),
postData.getUpdatedAt(), postData.isLiked(),
postData.getImages().stream().map(Image::getImageIdentifier).toList()
);
}
}
30 changes: 30 additions & 0 deletions src/main/java/daybyquest/post/presentation/PostCommandApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package daybyquest.post.presentation;

import daybyquest.auth.Authorization;
import daybyquest.auth.UserId;
import daybyquest.post.application.SavePostService;
import daybyquest.post.dto.request.SavePostRequest;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
public class PostCommandApi {

private final SavePostService savePostService;

public PostCommandApi(final SavePostService savePostService) {
this.savePostService = savePostService;
}

@PostMapping("/post")
@Authorization
public ResponseEntity<Long> savePost(@UserId final Long loginId, @RequestPart SavePostRequest request,
@RequestPart List<MultipartFile> files) {
final Long postId = savePostService.invoke(loginId, request, files);
return ResponseEntity.ok(postId);
}
}
27 changes: 27 additions & 0 deletions src/main/java/daybyquest/post/presentation/PostQueryApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package daybyquest.post.presentation;

import daybyquest.auth.Authorization;
import daybyquest.auth.UserId;
import daybyquest.post.application.GetPostService;
import daybyquest.post.dto.response.PostResponse;
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.RestController;

@RestController
public class PostQueryApi {

private final GetPostService getPostService;

public PostQueryApi(final GetPostService getPostService) {
this.getPostService = getPostService;
}

@GetMapping("/post/{postId}")
@Authorization
public ResponseEntity<PostResponse> getPost(@UserId final Long loginId, @PathVariable final Long postId) {
final PostResponse response = getPostService.invoke(loginId, postId);
return ResponseEntity.ok(response);
}
}
6 changes: 6 additions & 0 deletions src/main/java/daybyquest/post/query/PostDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package daybyquest.post.query;

public interface PostDao {

PostData getByPostId(final Long userId, final Long postId);
}
48 changes: 48 additions & 0 deletions src/main/java/daybyquest/post/query/PostDaoQuerydslImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package daybyquest.post.query;

import static daybyquest.image.vo.QImage.image;
import static daybyquest.like.domain.QPostLike.postLike;
import static daybyquest.post.domain.QPost.post;

import com.querydsl.core.types.Projections;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import daybyquest.global.error.exception.NotExistPostException;
import daybyquest.image.vo.Image;
import java.util.List;
import org.springframework.stereotype.Repository;

@Repository
public class PostDaoQuerydslImpl implements PostDao {

private final JPAQueryFactory factory;

public PostDaoQuerydslImpl(final JPAQueryFactory factory) {
this.factory = factory;
}

@Override
public PostData getByPostId(final Long userId, final Long postId) {
final PostData postData = factory.select(Projections.constructor(PostData.class,
post.userId,
post.id,
post.content,
post.updatedAt,
JPAExpressions.selectFrom(postLike)
.where(postLike.userId.eq(userId).and(postLike.postId.eq(postId)))
.exists()))
.from(post)
.where(post.id.eq(postId))
.fetchOne();
if (postData == null) {
throw new NotExistPostException();
}
final List<Image> images = factory.select(image)
.from(post)
.leftJoin(post.images, image)
.where(post.id.eq(postId))
.fetch();
postData.setImages(images);
return postData;
}
}
Loading
Loading