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/BE] 예외 상황시 예외 코드를 반환 #298

Merged
merged 6 commits into from
Aug 1, 2022
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
4 changes: 4 additions & 0 deletions backend/src/docs/asciidoc/products.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ operation::products-get[snippets='http-request,http-response']
- 없을 경우 등록 순서

operation::products-page-get[snippets='http-request,http-response']

=== 특정 제품의 사용자 통계를 조회

operation::products-member-statistics-get[snippets='http-request,http-response']
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.woowacourse.f12.dto.request.inventoryproduct.ProfileProductRequest;
import com.woowacourse.f12.dto.response.inventoryproduct.InventoryProductsResponse;
import com.woowacourse.f12.exception.badrequest.InvalidProfileProductException;
import com.woowacourse.f12.exception.notfound.InventoryItemNotFoundException;
import com.woowacourse.f12.exception.notfound.InventoryProductNotFoundException;
import com.woowacourse.f12.exception.notfound.MemberNotFoundException;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -51,7 +51,7 @@ private void updateProfileProduct(final ProfileProductRequest profileProductRequ

private void updateProfileProduct(final Long inventoryItemId, final boolean selected) {
final InventoryProduct inventoryProduct = inventoryProductRepository.findById(inventoryItemId)
.orElseThrow(InventoryItemNotFoundException::new);
.orElseThrow(InventoryProductNotFoundException::new);
inventoryProduct.updateSelected(selected);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import com.woowacourse.f12.domain.member.JobType;
import com.woowacourse.f12.domain.member.Member;
import com.woowacourse.f12.domain.member.MemberRepository;
import com.woowacourse.f12.dto.CareerLevelConstant;
import com.woowacourse.f12.dto.JobTypeConstant;
import com.woowacourse.f12.dto.request.member.MemberRequest;
import com.woowacourse.f12.dto.request.member.MemberSearchRequest;
import com.woowacourse.f12.dto.response.member.MemberPageResponse;
import com.woowacourse.f12.dto.response.member.MemberResponse;
import com.woowacourse.f12.exception.badrequest.InvalidProfileArgumentException;
import com.woowacourse.f12.exception.notfound.MemberNotFoundException;
import com.woowacourse.f12.presentation.member.CareerLevelConstant;
import com.woowacourse.f12.presentation.member.JobTypeConstant;
import java.util.Objects;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package com.woowacourse.f12.application.product;

import com.woowacourse.f12.domain.member.CareerLevel;
import com.woowacourse.f12.domain.member.JobType;
import com.woowacourse.f12.domain.product.Category;
import com.woowacourse.f12.domain.product.Product;
import com.woowacourse.f12.domain.product.ProductRepository;
import com.woowacourse.f12.domain.review.CareerLevelCount;
import com.woowacourse.f12.domain.review.JobTypeCount;
import com.woowacourse.f12.domain.review.MemberInfoStatistics;
import com.woowacourse.f12.domain.review.ReviewRepository;
import com.woowacourse.f12.dto.response.product.ProductPageResponse;
import com.woowacourse.f12.dto.response.product.ProductResponse;
import com.woowacourse.f12.exception.notfound.KeyboardNotFoundException;
import com.woowacourse.f12.dto.response.product.ProductStatisticsResponse;
import com.woowacourse.f12.exception.notfound.ProductNotFoundException;
import com.woowacourse.f12.presentation.product.CategoryConstant;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
Expand All @@ -17,14 +26,16 @@
public class ProductService {

private final ProductRepository productRepository;
private final ReviewRepository reviewRepository;

public ProductService(final ProductRepository productRepository) {
public ProductService(final ProductRepository productRepository, final ReviewRepository reviewRepository) {
this.productRepository = productRepository;
this.reviewRepository = reviewRepository;
}

public ProductResponse findById(final Long id) {
final Product product = productRepository.findById(id)
.orElseThrow(KeyboardNotFoundException::new);
.orElseThrow(ProductNotFoundException::new);
return ProductResponse.from(product);
}

Expand All @@ -36,4 +47,26 @@ public ProductPageResponse findPage(final CategoryConstant categoryConstant, fin
final Category category = categoryConstant.toCategory();
return ProductPageResponse.from(productRepository.findPageByCategory(category, pageable));
}

public ProductStatisticsResponse calculateMemberStatisticsById(final Long productId) {
if (!productRepository.existsById(productId)) {
throw new ProductNotFoundException();
}
final Map<CareerLevel, Double> careerLevel = calculateWithCareerLevel(productId);
final Map<JobType, Double> jobType = calculateWithJobType(productId);
return ProductStatisticsResponse.of(careerLevel, jobType);
}

private Map<CareerLevel, Double> calculateWithCareerLevel(final Long productId) {
final List<CareerLevelCount> careerLevelCounts = reviewRepository.findCareerLevelCountByProductId(productId);
final MemberInfoStatistics<CareerLevelCount, CareerLevel> careerLevelStatistics = new MemberInfoStatistics<>(
careerLevelCounts);
return careerLevelStatistics.calculateStatistics(CareerLevel.values());
}

private Map<JobType, Double> calculateWithJobType(final Long productId) {
final List<JobTypeCount> jobTypeCounts = reviewRepository.findJobTypeCountByProductId(productId);
final MemberInfoStatistics<JobTypeCount, JobType> jobTypeStatistics = new MemberInfoStatistics<>(jobTypeCounts);
return jobTypeStatistics.calculateStatistics(JobType.values());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
import com.woowacourse.f12.dto.response.review.ReviewWithProductPageResponse;
import com.woowacourse.f12.exception.badrequest.AlreadyWrittenReviewException;
import com.woowacourse.f12.exception.forbidden.NotAuthorException;
import com.woowacourse.f12.exception.notfound.KeyboardNotFoundException;
import com.woowacourse.f12.exception.notfound.MemberNotFoundException;
import com.woowacourse.f12.exception.notfound.ProductNotFoundException;
import com.woowacourse.f12.exception.notfound.ReviewNotFoundException;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
Expand Down Expand Up @@ -45,7 +45,7 @@ public Long saveReviewAndInventoryProduct(final Long productId, final Long membe
final Member member = memberRepository.findById(memberId)
.orElseThrow(MemberNotFoundException::new);
final Product product = productRepository.findById(productId)
.orElseThrow(KeyboardNotFoundException::new);
.orElseThrow(ProductNotFoundException::new);
final Long reviewId = saveReview(reviewRequest, member, product);
saveInventoryProduct(member, product);
return reviewId;
Expand Down Expand Up @@ -83,7 +83,7 @@ public ReviewPageResponse findPageByProductId(final Long productId, final Pageab

private void validateKeyboardExists(final Long productId) {
if (!productRepository.existsById(productId)) {
throw new KeyboardNotFoundException();
throw new ProductNotFoundException();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.woowacourse.f12.domain.member;

public enum CareerLevel {
public enum CareerLevel implements MemberInfo {

NONE,
JUNIOR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
import lombok.Getter;

@Getter
public enum JobType {
public enum JobType implements MemberInfo {

FRONTEND,
BACKEND,
MOBILE,
ETC
;
ETC;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.woowacourse.f12.domain.member;

public interface MemberInfo {

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.woowacourse.f12.domain.member;

import static com.woowacourse.f12.domain.member.QMember.member;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
Expand All @@ -12,7 +14,6 @@

public class MemberRepositoryCustomImpl extends QuerydslRepositorySupport implements MemberRepositoryCustom {

private final QMember member = QMember.member;
private final JPAQueryFactory jpaQueryFactory;

public MemberRepositoryCustomImpl(final JPAQueryFactory jpaQueryFactory) {
Expand All @@ -23,8 +24,7 @@ public MemberRepositoryCustomImpl(final JPAQueryFactory jpaQueryFactory) {
public Slice<Member> findBySearchConditions(final String keyword, final CareerLevel careerLevel,
final JobType jobType,
final Pageable pageable) {
final JPAQuery<Member> jpaQuery = jpaQueryFactory.select(member)
.from(member)
final JPAQuery<Member> jpaQuery = jpaQueryFactory.selectFrom(member)
.where(
containsKeyword(keyword),
eqCareerLevel(careerLevel),
Expand All @@ -37,7 +37,7 @@ public Slice<Member> findBySearchConditions(final String keyword, final CareerLe
}

private BooleanExpression containsKeyword(final String keyword) {
if (keyword == null || keyword.isBlank()) {
if (Objects.isNull(keyword) || keyword.isBlank()) {
return null;
}
return member.gitHubId.contains(keyword);
Expand All @@ -57,7 +57,7 @@ public BooleanExpression eqJobType(final JobType jobType) {
return member.jobType.eq(jobType);
}

private SliceImpl<Member> toSlice(final Pageable pageable, final List<Member> members) {
private Slice<Member> toSlice(final Pageable pageable, final List<Member> members) {
if (members.size() > pageable.getPageSize()) {
members.remove(members.size() - 1);
return new SliceImpl<>(members, pageable, true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.woowacourse.f12.domain.review;

import com.querydsl.core.annotations.QueryProjection;
import com.woowacourse.f12.domain.member.CareerLevel;
import com.woowacourse.f12.domain.member.MemberInfo;

public class CareerLevelCount implements Countable {

private final CareerLevel careerLevel;
private final long count;

@QueryProjection
public CareerLevelCount(final CareerLevel careerLevel, final long count) {
this.careerLevel = careerLevel;
this.count = count;
}

@Override
public MemberInfo getValue() {
return careerLevel;
}

@Override
public long getCount() {
return count;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.woowacourse.f12.domain.review;

import com.woowacourse.f12.domain.member.MemberInfo;

public interface Countable {

MemberInfo getValue();

long getCount();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.woowacourse.f12.domain.review;

import com.querydsl.core.annotations.QueryProjection;
import com.woowacourse.f12.domain.member.JobType;
import com.woowacourse.f12.domain.member.MemberInfo;

public class JobTypeCount implements Countable {

private final JobType jobType;
private final long count;

@QueryProjection
public JobTypeCount(final JobType jobType, final long count) {
this.jobType = jobType;
this.count = count;
}

@Override
public MemberInfo getValue() {
return jobType;
}

@Override
public long getCount() {
return count;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.woowacourse.f12.domain.review;

import com.woowacourse.f12.domain.member.MemberInfo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class MemberInfoStatistics<E extends Countable, T extends MemberInfo> {

private static final int DECIMAL_PLACE = 2;
private static final double DEFAULT_VALUE = 0.00;
private static final double DECIMAL = 10.0;
private final List<E> elements;

public MemberInfoStatistics(final List<E> elements) {
this.elements = elements;
}

public Map<T, Double> calculateStatistics(final T[] values) {
final long totalCount = calculateTotalCount();
return Arrays.stream(values)
.collect(Collectors.toMap(Function.identity(),
memberInfo -> calculateProportion(memberInfo, totalCount)));
}

private long calculateTotalCount() {
return elements.stream()
.mapToLong(Countable::getCount)
.sum();
}

private Double calculateProportion(final T t, final long totalCount) {
return elements.stream()
.filter(it -> it.getValue().equals(t))
.findAny()
.map(countable -> round(countable.getCount() / (double) totalCount))
.orElse(DEFAULT_VALUE);
}

private double round(double number) {
final double operand = Math.pow(DECIMAL, DECIMAL_PLACE);
return Math.round(number * operand) / operand;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface ReviewRepository extends JpaRepository<Review, Long> {
public interface ReviewRepository extends JpaRepository<Review, Long>, ReviewRepositoryCustom {

@Query("select r from Review r where r.product.id = :productId")
Slice<Review> findPageByProductId(Long productId, Pageable pageable);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.woowacourse.f12.domain.review;

import java.util.List;

public interface ReviewRepositoryCustom {

List<CareerLevelCount> findCareerLevelCountByProductId(Long productId);

List<JobTypeCount> findJobTypeCountByProductId(Long productId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.woowacourse.f12.domain.review;

import static com.woowacourse.f12.domain.member.QMember.member;
import static com.woowacourse.f12.domain.review.QReview.review;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.woowacourse.f12.domain.product.Product;
import java.util.List;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;

public class ReviewRepositoryCustomImpl extends QuerydslRepositorySupport implements ReviewRepositoryCustom {

private final JPAQueryFactory jpaQueryFactory;

public ReviewRepositoryCustomImpl(final JPAQueryFactory jpaQueryFactory) {
super(Product.class);
this.jpaQueryFactory = jpaQueryFactory;
}

@Override
public List<CareerLevelCount> findCareerLevelCountByProductId(final Long productId) {
return jpaQueryFactory.select(new QCareerLevelCount(member.careerLevel, member.id.count()))
.from(review)
.innerJoin(review.member, member)
.where(review.product.id.eq(productId))
.groupBy(member.careerLevel)
.fetch();
}

@Override
public List<JobTypeCount> findJobTypeCountByProductId(final Long productId) {
return jpaQueryFactory.select(new QJobTypeCount(member.jobType, member.id.count()))
.from(review)
.innerJoin(review.member, member)
.where(review.product.id.eq(productId))
.groupBy(member.jobType)
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.woowacourse.f12.dto.request.member;

import com.woowacourse.f12.dto.CareerLevelConstant;
import com.woowacourse.f12.dto.JobTypeConstant;
import com.woowacourse.f12.presentation.member.CareerLevelConstant;
import com.woowacourse.f12.presentation.member.JobTypeConstant;
import javax.validation.constraints.NotNull;
import lombok.Getter;

Expand Down
Loading