Skip to content

Commit

Permalink
Merge pull request #35 from Team-baebae/feature/question/#34
Browse files Browse the repository at this point in the history
Feature/question/#34
  • Loading branch information
jihyo-j authored May 5, 2024
2 parents 24e5a77 + ca21cf5 commit 8b2b404
Show file tree
Hide file tree
Showing 32 changed files with 1,033 additions and 4 deletions.
4 changes: 2 additions & 2 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules/com.web.baebae-BE.main.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions baebae-BE/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
//Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.web.baebaeBE.application.answer;

import com.web.baebaeBE.domain.answer.exception.AnswerError;
import com.web.baebaeBE.domain.answer.service.AnswerService;
import com.web.baebaeBE.domain.member.exception.MemberError;
import com.web.baebaeBE.global.error.exception.BusinessException;
import com.web.baebaeBE.global.image.s3.S3ImageStorageService;
import com.web.baebaeBE.infra.answer.entity.Answer;
import com.web.baebaeBE.infra.answer.repository.AnswerMapper;
import com.web.baebaeBE.infra.member.entity.Member;
import com.web.baebaeBE.infra.member.repository.MemberRepository;
import com.web.baebaeBE.infra.question.entity.Question;
import com.web.baebaeBE.infra.question.repository.QuestionRepository;
import com.web.baebaeBE.presentation.answer.dto.AnswerCreateRequest;
import com.web.baebaeBE.presentation.answer.dto.AnswerDetailResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;


@Service
@RequiredArgsConstructor
public class AnswerApplication {
private final AnswerService answerService;
private final AnswerMapper answerMapper;
private final MemberRepository memberRepository;
private final QuestionRepository questionRepository;
private final S3ImageStorageService s3ImageStorageService;

public AnswerDetailResponse createAnswer(AnswerCreateRequest request, Long memberId) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new BusinessException(MemberError.NOT_EXIST_MEMBER));
Question question = questionRepository.findById(request.getQuestionId())
.orElseThrow(() -> new BusinessException(AnswerError.NO_EXIST_QUESTION));
List<String> imageUrls = uploadImages(request.getImageFiles());
Answer answerEntity = answerMapper.toEntity(request, question, member);
Answer savedAnswerEntity = answerService.createAnswer(answerEntity, request.getImageFiles());
return answerMapper.toDomain(savedAnswerEntity);
}

private List<String> uploadImages(List<MultipartFile> files) {
List<String> urls = new ArrayList<>();
for (MultipartFile file : files) {
try {
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
String path = "answers/images";
InputStream inputStream = file.getInputStream();
String url = s3ImageStorageService.uploadFile(path, fileName, inputStream, file.getSize(), file.getContentType());
urls.add(url);
} catch (IOException e) {
throw new BusinessException(AnswerError.IMAGE_PROCESSING_ERROR);
}
}
return urls;
}

public Page<AnswerDetailResponse> getAllAnswers(Long memberId, Pageable pageable) {
Page<Answer> answerPage = answerService.getAllAnswers(memberId, pageable);
return answerPage.map(answerMapper::toDomain);
}

public AnswerDetailResponse updateAnswer(Long answerId, AnswerCreateRequest request, MultipartFile[] imageFiles) {
Answer updatedAnswer = answerService.updateAnswer(answerId, request, imageFiles);
return answerMapper.toDomain(updatedAnswer);
}

public void deleteAnswer(Long answerId) {
answerService.deleteAnswer(answerId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.web.baebaeBE.application.question;

import com.web.baebaeBE.domain.member.exception.MemberError;
import com.web.baebaeBE.domain.question.service.QuestionService;
import com.web.baebaeBE.global.error.exception.BusinessException;
import com.web.baebaeBE.infra.member.entity.Member;
import com.web.baebaeBE.infra.member.repository.MemberRepository;
import com.web.baebaeBE.infra.question.entity.Question;
import com.web.baebaeBE.infra.question.repository.QuestionMapper;
import com.web.baebaeBE.presentation.question.dto.QuestionCreateRequest;
import com.web.baebaeBE.presentation.question.dto.QuestionDetailResponse;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Transactional
public class QuestionApplication {
private final QuestionService questionService;
private final QuestionMapper questionMapper;
private final MemberRepository memberRepository;

public QuestionDetailResponse createQuestion(QuestionCreateRequest request, Long senderId, Long receiverId, String token) {
Member sender = memberRepository.findById(senderId)
.orElseThrow(() -> new BusinessException(MemberError.NOT_EXIST_MEMBER));
Member receiver = memberRepository.findById(receiverId)
.orElseThrow(() -> new BusinessException(MemberError.NOT_EXIST_MEMBER));

Question questionEntity = questionMapper.toEntity(request, sender, receiver);
Question savedQuestionEntity = questionService.createQuestion(questionEntity);
return questionMapper.toDomain(savedQuestionEntity, token);
}

public Page<QuestionDetailResponse> getAllQuestions(Long memberId, Pageable pageable, boolean isSender) {
Page<Question> questionPage;
if (isSender) {
questionPage = questionService.getQuestionsBySenderId(memberId, pageable);
} else {
questionPage = questionService.getQuestionsByReceiverId(memberId, pageable);
}
return questionPage.map(question -> questionMapper.toDomain(question, "appropriate token here"));
}

public QuestionDetailResponse updateQuestion(Long questionId, String content, String token) {
Question updatedQuestion = questionService.updateQuestion(questionId, content);
return questionMapper.toDomain(updatedQuestion, token);
}

public void deleteQuestion(Long questionId) {
questionService.deleteQuestion(questionId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.web.baebaeBE.domain.answer.exception;

import com.web.baebaeBE.global.error.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum AnswerError implements ErrorCode {

NO_EXIST_MEMBER(HttpStatus.NOT_FOUND, "M-001", "존재하지 않는 회원입니다."),
NO_EXIST_ANSWER(HttpStatus.NOT_FOUND, "M-002", "존재하지 않는 답변입니다."),
NO_EXIST_QUESTION(HttpStatus.NOT_FOUND, "M-003", "존재하지 않는 질문입니다."),
IMAGE_PROCESSING_ERROR(HttpStatus.NOT_FOUND, "M-004", "이미지 업로드 오류입니다.");

private final HttpStatus httpStatus;
private final String errorCode;
private final String message;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.web.baebaeBE.domain.answer.service;

import com.web.baebaeBE.domain.answer.exception.AnswerError;
import com.web.baebaeBE.global.error.exception.BusinessException;
import com.web.baebaeBE.global.image.s3.S3ImageStorageService;
import com.web.baebaeBE.infra.answer.entity.Answer;
import com.web.baebaeBE.infra.answer.repository.AnswerRepository;
import com.web.baebaeBE.presentation.answer.dto.AnswerCreateRequest;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Service
public class AnswerService {
private final AnswerRepository answerRepository;
private final S3ImageStorageService s3ImageStorageService;

public Answer createAnswer(Answer answer, List<MultipartFile> imageFiles) {
List<String> urls = new ArrayList<>();
try {
for (MultipartFile file : imageFiles) {
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
String path = "answers/images";
InputStream inputStream = file.getInputStream();
String url = s3ImageStorageService.uploadFile(path, fileName, inputStream, file.getSize(), file.getContentType());
urls.add(url);
}
} catch (IOException e) {
throw new BusinessException(AnswerError.IMAGE_PROCESSING_ERROR);
}
answer.setImageFiles(urls);
return answerRepository.save(answer);
}

@Transactional
public Page<Answer> getAllAnswers(Long memberId, Pageable pageable) {
return answerRepository.findAllByMemberId(memberId, pageable);
}

@Transactional
public Answer updateAnswer(Long answerId, AnswerCreateRequest request, MultipartFile[] imageFiles) {
Answer answer = answerRepository.findByAnswerId(answerId)
.orElseThrow(() -> new BusinessException(AnswerError.NO_EXIST_ANSWER));

List<String> urls = new ArrayList<>();
try {
for (MultipartFile file : imageFiles) {
String fileName = file.getOriginalFilename();
String path = "answers/images";
InputStream inputStream = file.getInputStream();
String url = s3ImageStorageService.uploadFile(path, fileName, inputStream, file.getSize(), file.getContentType());
urls.add(url);
}
} catch (IOException e) {
throw new BusinessException(AnswerError.IMAGE_PROCESSING_ERROR);
}
answer.setImageFiles(urls);

answer.setContent(request.getContent());
answer.setLinkAttachments(request.getLinkAttachments());
answer.setMusicName(request.getMusicName());

return answerRepository.save(answer);
}

@Transactional
public void deleteAnswer(Long answerId) {
Answer answer = answerRepository.findByAnswerId(answerId)
.orElseThrow(() -> new IllegalArgumentException("No answer found with id " + answerId));
answer.getImageFiles().forEach(url -> {
String fileName = url.substring(url.lastIndexOf("/") + 1);
s3ImageStorageService.deleteFile("answers/images", fileName);
});
answerRepository.delete(answer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;


@Getter
@RequiredArgsConstructor
public enum MemberError implements ErrorCode {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.web.baebaeBE.domain.question.exception;

import com.web.baebaeBE.global.error.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum QuestionError implements ErrorCode {
NO_EXIST_MEMBER(HttpStatus.NOT_FOUND, "M-001", "존재하지 않는 회원입니다."),
UNFILLED_QUESTION(HttpStatus.NOT_FOUND, "M-002", "빈칸으로 제출할 수 없습니다."),
NO_EXIST_QUESTION(HttpStatus.NOT_FOUND, "M-003", "존재하지 않는 질문입니다.");

private final HttpStatus httpStatus;
private final String errorCode;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.web.baebaeBE.domain.question.service;

import com.web.baebaeBE.domain.question.exception.QuestionError;
import com.web.baebaeBE.global.error.exception.BusinessException;
import com.web.baebaeBE.infra.question.entity.Question;
import com.web.baebaeBE.infra.question.repository.QuestionRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class QuestionService {
private final QuestionRepository questionRepository;

@Transactional
public Question createQuestion(Question question) {
return questionRepository.save(question);
}

@Transactional(readOnly = true)
public Page<Question> getQuestionsBySenderId(Long senderId, Pageable pageable) {
return questionRepository.findAllBySenderId(senderId, pageable);
}

@Transactional(readOnly = true)
public Page<Question> getQuestionsByReceiverId(Long receiverId, Pageable pageable) {
return questionRepository.findAllByReceiverId(receiverId, pageable);
}

@Transactional
public Question updateQuestion(Long questionId, String content) {
Question questionEntity = questionRepository.findById(questionId)
.orElseThrow(() -> new BusinessException(QuestionError.NO_EXIST_QUESTION));
questionEntity.updateContent(content);
return questionRepository.save(questionEntity);
}

@Transactional
public void deleteQuestion(Long questionId) {
Question questionEntity = questionRepository.findById(questionId)
.orElseThrow(() -> new BusinessException(QuestionError.NO_EXIST_QUESTION));
questionRepository.delete(questionEntity);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.web.baebaeBE.global.converter;

import jakarta.persistence.AttributeConverter;

import java.util.Arrays;
import java.util.List;

public class StringListConverter implements AttributeConverter<List<String>, String> {
@Override
public String convertToDatabaseColumn(List<String> attribute) {
return (attribute == null || attribute.isEmpty()) ? "" : String.join(",", attribute);
}

@Override
public List<String> convertToEntityAttribute(String dbData) {
return (dbData == null || dbData.trim().isEmpty()) ? null : Arrays.asList(dbData.split(","));
}
}
Loading

0 comments on commit 8b2b404

Please sign in to comment.