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/update answer #104

Merged
merged 3 commits into from
May 21, 2024
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
2 changes: 1 addition & 1 deletion .idea/dbnavigator.xml

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.web.baebaeBE.domain.categorized.answer.controller;

import com.web.baebaeBE.domain.categorized.answer.controller.api.CategorizedAnswerApi;
import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerRequest;
import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse;
import com.web.baebaeBE.domain.categorized.answer.service.CategorizedAnswerService;
import com.web.baebaeBE.global.authorization.annotation.AuthorizationAnswer;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/categorizedAnswer")
public class CategorizedAnswerController implements CategorizedAnswerApi {

private final CategorizedAnswerService categorizedAnswerService;


@GetMapping("{answerId}")
@AuthorizationAnswer
public ResponseEntity<List<CategorizedAnswerResponse.CategoryInformationResponse>> getCategoriesByAnswerId(
@PathVariable Long answerId
) {
return ResponseEntity.ok(categorizedAnswerService.getCategoriesByAnswerId(answerId));
}

@PutMapping("/{answerId}")
@AuthorizationAnswer
public ResponseEntity<Void> updateCategoriesByAnswerId(@PathVariable Long answerId, @RequestBody CategorizedAnswerRequest.CategoryList categoryIds) {
categorizedAnswerService.updateCategoriesByAnswerId(answerId, categoryIds);
return ResponseEntity.noContent().build();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.web.baebaeBE.domain.categorized.answer.controller.api;

import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerRequest;
import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@Tag(name = "CategorizedAnswer", description = "카테고리 내부의 피드에 관련된 API")
public interface CategorizedAnswerApi {

@Operation(summary = "피드가 속한 카테고리 조회",
description = "Answer ID를 받아 해당 Answer에 연결된 모든 카테고리를 조회합니다.",
security = @SecurityRequirement(name = "bearerAuth")
)
@Parameter(
in = ParameterIn.HEADER,
name = "Authorization", required = true,
schema = @Schema(type = "string"),
description = "Bearer [Access 토큰]")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공")
})
@GetMapping("{answerId}")
ResponseEntity<List<CategorizedAnswerResponse.CategoryInformationResponse>> getCategoriesByAnswerId(
@Parameter(description = "Answer의 ID", required = true) @PathVariable Long answerId
);


@Operation(summary = "피드가 속한 카테고리 수정",
description = "Answer ID와 Category ID 리스트를 받아 피드가 속할 카테고리 정보를 수정합니다.",
security = @SecurityRequirement(name = "bearerAuth")
)
@Parameter(
in = ParameterIn.HEADER,
name = "Authorization", required = true,
schema = @Schema(type = "string"),
description = "Bearer [Access 토큰]")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "수정 성공")
})
@PutMapping("/{answerId}")
public ResponseEntity<Void> updateCategoriesByAnswerId(@PathVariable Long answerId, @RequestBody CategorizedAnswerRequest.CategoryList categoryIds) ;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.web.baebaeBE.domain.categorized.answer.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

public class CategorizedAnswerRequest {

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class CategoryList{
private List<Long> categoryIds;

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

import com.web.baebaeBE.domain.category.dto.CategoryResponse;
import com.web.baebaeBE.domain.category.entity.Category;
import lombok.*;

import java.util.List;
import java.util.stream.Collectors;

public class CategorizedAnswerResponse {

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class CategoryInformationResponse {
private Long categoryId;
private String categoryName;
private String categoryImage;

public static CategorizedAnswerResponse.CategoryInformationResponse of(Category category) {
return CategorizedAnswerResponse.CategoryInformationResponse.builder()
.categoryId(category.getId())
.categoryName(category.getCategoryName())
.categoryImage(category.getCategoryImage())
.build();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.web.baebaeBE.domain.category.entity.Category;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import java.time.LocalDateTime;

Expand All @@ -18,14 +20,17 @@ public class CategorizedAnswer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "categorized_answer_id")
private Long id;

@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private Category category;

@ManyToOne
@JoinColumn(name = "answer_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private Answer answer;

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface CategorizedAnswerRepository extends JpaRepository<CategorizedAnswer, Long> {
Page<CategorizedAnswer> findByAnswer_Member_IdAndCategory_Id(Long memberId, Long categoryId, Pageable pageable);
Page<CategorizedAnswer> findByAnswer_Member_Id(Long memberId, Pageable pageable);
List<CategorizedAnswer> findAllByAnswerId(Long answerId);
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,56 @@
package com.web.baebaeBE.domain.categorized.answer.service;

import com.web.baebaeBE.domain.answer.dto.AnswerDetailResponse;
import com.web.baebaeBE.domain.answer.entity.Answer;
import com.web.baebaeBE.domain.answer.exception.AnswerError;
import com.web.baebaeBE.domain.answer.repository.AnswerMapper;
import com.web.baebaeBE.domain.answer.repository.AnswerRepository;
import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerRequest;
import com.web.baebaeBE.domain.categorized.answer.dto.CategorizedAnswerResponse;
import com.web.baebaeBE.domain.categorized.answer.entity.CategorizedAnswer;
import com.web.baebaeBE.domain.categorized.answer.repository.CategorizedAnswerRepository;
import com.web.baebaeBE.domain.category.entity.Category;
import com.web.baebaeBE.domain.category.exception.CategoryException;
import com.web.baebaeBE.domain.category.repository.CategoryRepository;
import com.web.baebaeBE.global.error.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
@Transactional
public class CategorizedAnswerService {
private final CategorizedAnswerRepository categorizedAnswerRepository;
private final AnswerMapper answerMapper;
private final CategoryRepository categoryRepository;
private final AnswerRepository answerRepository;

public List<CategorizedAnswerResponse.CategoryInformationResponse> getCategoriesByAnswerId(Long answerId) {
List<CategorizedAnswer> categorizedAnswers = categorizedAnswerRepository.findAllByAnswerId(answerId);

for(CategorizedAnswer categorizedAnswer : categorizedAnswers) {
log.info("categorizedAnswer : {}", categorizedAnswer.getCategory().getId());
}
List<CategorizedAnswerResponse.CategoryInformationResponse> categoryInformationResponses = new ArrayList<>();

for(CategorizedAnswer categorizedAnswer : categorizedAnswers) {
categoryInformationResponses.add
(CategorizedAnswerResponse.CategoryInformationResponse.of(categorizedAnswer.getCategory()));
}

return categoryInformationResponses;
}


public Page<AnswerDetailResponse> getAnswersByMemberAndCategory(Long memberId, Long categoryId, Pageable pageable) {
Page<CategorizedAnswer> categorizedAnswers;
Expand All @@ -27,6 +60,49 @@ public Page<AnswerDetailResponse> getAnswersByMemberAndCategory(Long memberId, L
categorizedAnswers = categorizedAnswerRepository.findByAnswer_Member_IdAndCategory_Id(memberId, categoryId, pageable);

return categorizedAnswers.map(categorizedAnswer -> answerMapper.toDomain(categorizedAnswer.getAnswer()));
}


@Transactional
public void updateCategoriesByAnswerId(Long answerId, CategorizedAnswerRequest.CategoryList categoryList) {
List<CategorizedAnswer> existingCategorizedAnswers = categorizedAnswerRepository.findAllByAnswerId(answerId);
List<Long> existingCategoryIds = existingCategorizedAnswers.stream()
.map(categorizedAnswer -> categorizedAnswer.getCategory().getId())
.collect(Collectors.toList());

List<Long> newCategoryIds = categoryList.getCategoryIds();

// 추가해야 할 카테고리 ID 계산
List<Long> toAddCategoryIds = newCategoryIds.stream()
.filter(categoryId -> !existingCategoryIds.contains(categoryId))
.collect(Collectors.toList());

// 삭제해야 할 카테고리 ID 계산
List<Long> toRemoveCategoryIds = existingCategoryIds.stream()
.filter(categoryId -> !newCategoryIds.contains(categoryId))
.collect(Collectors.toList());

Answer answer = answerRepository.findByAnswerId(answerId)
.orElseThrow(() -> new BusinessException(AnswerError.NO_EXIST_ANSWER));

// 새로운 카테고리 ID에 대해 반복하여 CategorizedAnswer를 생성하고 저장
for (Long categoryId : toAddCategoryIds) {
Category category = categoryRepository.findById(categoryId)
.orElseThrow(() -> new BusinessException(CategoryException.CATEGORY_NOT_FOUND));
CategorizedAnswer newCategorizedAnswer = CategorizedAnswer.builder()
.category(category)
.answer(answer)
.build();
categorizedAnswerRepository.save(newCategorizedAnswer);
}

// 삭제해야 할 카테고리 ID에 대해 반복하여 해당 CategorizedAnswer를 삭제
for (Long categoryId : toRemoveCategoryIds) {
CategorizedAnswer categorizedAnswerToRemove = existingCategorizedAnswers.stream()
.filter(categorizedAnswer -> categorizedAnswer.getCategory().getId().equals(categoryId))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No CategorizedAnswer found for the given category id"));
categorizedAnswerRepository.delete(categorizedAnswerToRemove);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import com.web.baebaeBE.domain.login.service.OAuth2UserCustomService;

import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
Expand All @@ -22,8 +25,10 @@
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.web.baebaeBE.global.security.SecurityConstants.NO_AUTH_LIST;
import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toH2Console;
Expand All @@ -34,8 +39,9 @@
@EnableMethodSecurity
public class SecurityConfig {
private final JwtTokenProvider jwtTokenProvider;
private final OAuth2UserCustomService oAuth2UserCustomService;

@Value("${allowed.origins}")
private String[] allowedOrigins;


// Spring Security 제외 목록 (인증,인가 검사 제외)
Expand Down Expand Up @@ -84,7 +90,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173", "https://api.flipit.co.kr", "https://www.flipit.co.kr", "https://flipit.co.kr")); // 허용할 오리진 설정
configuration.setAllowedOrigins(List.of(allowedOrigins)); // 허용할 오리진 설정
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH")); // 허용할 HTTP 메소드 설정
configuration.setAllowedHeaders(Collections.singletonList("*")); // 허용할 HTTP 헤더 설정
configuration.setAllowCredentials(true); // 쿠키를 포함한 요청 허용 설정
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.web.baebaeBE.global.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${allowed.origins}")
private String[] allowedOrigins;

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:5173", "https://api.flipit.co.kr", "https://www.flipit.co.kr", "https://flipit.co.kr")
.allowedOrigins(allowedOrigins)
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*") // 모든 헤더 허용
.allowCredentials(true)
Expand Down
Loading