Skip to content

Commit

Permalink
[FE] team-03브랜치에서 dev-FE로 최신화
Browse files Browse the repository at this point in the history
commit a773a14
Merge: 2bc424b 1276077
Author: HYUNJUN SON <55608425+guswns1659@users.noreply.github.com>
Date:   Sun Apr 24 22:54:48 2022 +0900

    Merge pull request #59 from Louie-03/dev-BE

    [Team-03][BE][루이&쿠킴] - 특정 음식 타입 조회 기능

commit 2bc424b
Author: Jwu <sju02048@naver.com>
Date:   Sun Apr 24 17:06:36 2022 +0900

    [team-03][FE][쥬&도리] 1주차 두 번째 PR: 컴포넌트 단위 설계 (codesquad-members-2022#66)

    * Chore: 초기개발환경

    Chore: CRA 초기구성

    Chore: eslint 구성

    Chore: prettier 구성

    * Style: App.js -> jsx 수정

    * [FE] team-03브랜치에서 dev-FE로 최신화 (#14)

    * Feat: 프로젝트 초기 세팅

    ref: #8

    * [team-03][FE][쥬&도리] 1주차 첫 PR: 프로젝트 환경설정 및 설계 (#25)

    * [공통] Issues, PR templates 와 프로젝트 소개 README.md 추가 (#2)

    * Docs: 프로젝트 및 팀원 소개(readme.md)

    * Chore: Issues, PR templates 추가

    ref: #1

    Co-authored-by: “Louie-03” <dhdustnr0134@naver.com>

    * Docs: 팀원 수정

    - 팀원 한 마디 추가

    * Chore: 초기개발환경

    Chore: CRA 초기구성

    Chore: eslint 구성

    Chore: prettier 구성

    * Style: App.js -> jsx 수정

    Co-authored-by: kukim <57086195+ku-kim@users.noreply.github.com>
    Co-authored-by: “Louie-03” <dhdustnr0134@naver.com>

    Co-authored-by: Louie <dhdustnr0134@naver.com>
    Co-authored-by: HYUNJUN SON <55608425+guswns1659@users.noreply.github.com>
    Co-authored-by: Jwu <sju02048@naver.com>
    Co-authored-by: kukim <57086195+ku-kim@users.noreply.github.com>

    * [FE] MealContainer 레이아웃 작성 (#16)

    * Chore: vscode debugger .gitingore에추가

    * Chore: axios 라이브러리 추가

    * Feat: MealContainer

    - MealHeader
    - Carousel -> MealCard여러개

    * Refactor: useMemo 삭제

    * Chore: TODO 주석 작성

    - 컴포넌트 분리
    - stlye 코드 분리
    - status 코드

    * [FE] BestMealContainer 레이아웃 작성 (#18)

    * Chore: Prettier useTabs true로 수정

    * Chore: axios 라이브러리 추가

    * Feat: BestMealContainer 기본 레이아웃

    Co-authored-by: YUNHO <kimyouknow@naver.com>

    * [FE] components단위로 파일 분리 (#20)

    * Refactor: App.jsx에서 BestMealContainer import수정

    * Refactor: MealContainer에서 Loader와 MealCard 분리

    * Chore: components 폴더 관리

    * [FE] 페어 리팩토링 (#21)

    * Refactor: mockServerURL .env파일에서 관리

    - constant폴더에서 관리

    * Feat: setDefaultImage()함수 추가

    - 인자로 image url을 받아서 false면 default이미지로 반환

    * Chore: 폴더경로 상대경로에서 절대경로로 변경

    - jsconfig.json파일 설정

    Co-authored-by: YUNHO <kimyouknow@naver.com>
    Co-authored-by: Louie <dhdustnr0134@naver.com>
    Co-authored-by: HYUNJUN SON <55608425+guswns1659@users.noreply.github.com>
    Co-authored-by: kukim <57086195+ku-kim@users.noreply.github.com>

commit 1276077
Merge: e8eca13 68b6e96
Author: Louie <dhdustnr0134@naver.com>
Date:   Fri Apr 22 11:03:53 2022 +0900

    Merge pull request #22 from Louie-03/BE-feature-GET_api_products_meal_type

    [BE] 특정 음식 타입 조회 기능

commit 68b6e96
Author: Louie <dhdustnr0134@naver.com>
Date:   Fri Apr 22 10:39:03 2022 +0900

    Fix: DiscountPolicy NPE 문제 해결

    - DiscountPolicy가 존재하지 않는 상품의 인수 테스트 추가

    Co-authored-by: “ku-kim” <kukim.dev@gmail.com>

commit 5335f2e
Author: Louie <dhdustnr0134@naver.com>
Date:   Fri Apr 22 10:37:09 2022 +0900

    Refactor: 계산 로직의 책임을 Product에서 DiscountPolicy로 위임

    - 기존 Product의 계산 로직을 DiscountPolicy의 calculateFixedPrice 메서드로 옮겼다.

    Co-authored-by: “ku-kim” <kukim.dev@gmail.com>

commit 1e27697
Author: “kukim” <kukim.dev@gmail.com>
Date:   Thu Apr 21 17:47:25 2022 +0900

    Feat: meal type으로 음식 조회가 되지 않는 경우 조회 실패 구현(404 NOT FOUND)

commit db93682
Author: “kukim” <kukim.dev@gmail.com>
Date:   Thu Apr 21 17:27:13 2022 +0900

    Fix: Product 객체 fixedPrice 계산 로직 수정

    - 기존 연산 괄호 실수 -> 올바르게 변경
    - 테스트 코드 추가

commit 3b74382
Author: Louie <dhdustnr0134@naver.com>
Date:   Thu Apr 21 16:53:35 2022 +0900

    Refactor: Entity와 Domain 객체 분리

    - Entity를 Domain 객체로 변경해주는 DomainEntityMapper 구현
    - 변경된 코드에 따른 테스트 코드 수정

    Co-authored-by: “ku-kim” <kukim.dev@gmail.com>

commit ca34b5a
Author: “kukim” <kukim.dev@gmail.com>
Date:   Thu Apr 21 15:17:09 2022 +0900

    Feat: 특정 Products의 meal type 조회 기능의 리포지토리 구현

    - 테스트 작성

    Co-authored-by: “Louie-03” <dhdustnr0134@naver.com>

commit 06cebb6
Author: “kukim” <kukim.dev@gmail.com>
Date:   Thu Apr 21 15:15:29 2022 +0900

    Feat: product, product_image, discount_policy 테이블 DDL과 더미데이터 생성, 테스트 환경 구분

    Co-authored-by: “Louie-03” <dhdustnr0134@naver.com>

commit feb2909
Author: “kukim” <kukim.dev@gmail.com>
Date:   Thu Apr 21 09:32:59 2022 +0900

    Refactor: Product <-> Response Dto 변환 위치를 컨트롤러에서 서비스로 변경

commit fc57090
Author: Jwu <sju02048@naver.com>
Date:   Wed Apr 20 21:07:11 2022 +0900

    [team-03][FE][쥬&도리] 1주차 첫 PR: 프로젝트 환경설정 및 설계 (#25)

    * [공통] Issues, PR templates 와 프로젝트 소개 README.md 추가 (#2)

    * Docs: 프로젝트 및 팀원 소개(readme.md)

    * Chore: Issues, PR templates 추가

    ref: #1

    Co-authored-by: “Louie-03” <dhdustnr0134@naver.com>

    * Docs: 팀원 수정

    - 팀원 한 마디 추가

    * Chore: 초기개발환경

    Chore: CRA 초기구성

    Chore: eslint 구성

    Chore: prettier 구성

    * Style: App.js -> jsx 수정

    Co-authored-by: kukim <57086195+ku-kim@users.noreply.github.com>
    Co-authored-by: “Louie-03” <dhdustnr0134@naver.com>

commit 4f385a1
Merge: a002511 e8eca13
Author: HYUNJUN SON <55608425+guswns1659@users.noreply.github.com>
Date:   Wed Apr 20 18:29:42 2022 +0900

    Merge pull request #20 from Louie-03/dev-BE

    [Team-03][BE] 쿠킴 & 루이 - 데이터베이스 설계, Mock API Server, 배포 아키텍처, 프로젝트 세팅

commit f4c13a4
Author: “kukim” <kukim.dev@gmail.com>
Date:   Wed Apr 20 17:32:01 2022 +0900

    Feat: 특정 Products의 meal type 조회 기능의 서비스 구현

    - 테스트 작성

commit 8e691b4
Author: “kukim” <kukim.dev@gmail.com>
Date:   Wed Apr 20 17:03:56 2022 +0900

    Feat: 특정 Products의 meal type 조회 기능의 컨트롤러 구현

    - API : GET /api/products?meal={value}
    - 컨트롤러 테스트 구현
    - 서비스 계층은 Mock 처리
    - Product 도메인 객체 생성
    - ProductsDtoMapper 객체 생성 : Product 도메인 <-> ProductsMealTypeResponse 변환

commit b9873d0
Author: “kukim” <kukim.dev@gmail.com>
Date:   Wed Apr 20 15:57:24 2022 +0900

    Test: 특정 Products의 meal type 조회 기능 테스트코드만 작성

    - 인수 테스트 작성

commit e8eca13
Author: Louie <dhdustnr0134@naver.com>
Date:   Wed Apr 20 14:24:31 2022 +0900

    Feat: 프로젝트 초기 세팅

    ref: #8
  • Loading branch information
jwu-ice committed Apr 25, 2022
1 parent f77bc3b commit eff924c
Show file tree
Hide file tree
Showing 25 changed files with 852 additions and 0 deletions.
27 changes: 27 additions & 0 deletions BE/src/main/java/sidedish/com/controller/ProductsController.java
@@ -0,0 +1,27 @@
package sidedish.com.controller;

import java.util.List;
import javax.validation.constraints.NotEmpty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import sidedish.com.controller.model.ProductMealTypeResponse;
import sidedish.com.service.ProductsService;

@RestController
@RequestMapping("/api/products")
public class ProductsController {

private final ProductsService productsService;

public ProductsController(ProductsService productsService) {
this.productsService = productsService;
}

@GetMapping
public List<ProductMealTypeResponse> findProductsMealType(
@RequestParam @NotEmpty String meal) {
return productsService.findByMealType(meal);
}
}
31 changes: 31 additions & 0 deletions BE/src/main/java/sidedish/com/controller/ProductsDtoMapper.java
@@ -0,0 +1,31 @@
package sidedish.com.controller;

import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
import sidedish.com.controller.model.ProductMealTypeResponse;
import sidedish.com.domain.Product;

@Component
public class ProductsDtoMapper {

public List<ProductMealTypeResponse> toProductsMealTypeResponseFromDomain(
List<Product> products) {
return products.stream()
.map(this::toProductMealTypeResponseFromDomain)
.collect(Collectors.toList());
}

private ProductMealTypeResponse toProductMealTypeResponseFromDomain(
Product product) {
return new ProductMealTypeResponse(
product.getId(),
product.getImages().get(0).getImageUrl(),
product.getProductName(),
product.getDescription(),
product.getFixedPrice(),
product.getOriginalPrice(),
product.getDiscountPolicy().getPolicyName());
}

}
@@ -0,0 +1,19 @@
package sidedish.com.controller.model;


import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class ProductMealTypeResponse {

private Long id;
private String image;
private String productName;
private String description;
private long fixedPrice;
private long originalPrice;
private String event;

}
16 changes: 16 additions & 0 deletions BE/src/main/java/sidedish/com/domain/DiscountPolicy.java
@@ -0,0 +1,16 @@
package sidedish.com.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class DiscountPolicy {

private String policyName;
private float discountRate;

public long calculateFixedPrice(long originalPrice) {
return (long) (originalPrice * ((100 - discountRate) / 100));
}
}
12 changes: 12 additions & 0 deletions BE/src/main/java/sidedish/com/domain/Image.java
@@ -0,0 +1,12 @@
package sidedish.com.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class Image {

private String imageUrl;

}
35 changes: 35 additions & 0 deletions BE/src/main/java/sidedish/com/domain/Product.java
@@ -0,0 +1,35 @@
package sidedish.com.domain;

import java.util.List;
import lombok.Getter;

@Getter
public class Product {
private Long id;
private DiscountPolicy discountPolicy;
private List<Image> images;
private String productName;
private String description;
private long fixedPrice;
private long originalPrice;
private String mealCategory;
private String bestCategory;

public Product(Long id, DiscountPolicy discountPolicy,
List<Image> images, String productName, String description, long originalPrice,
String mealCategory, String bestCategory) {
this.id = id;
this.discountPolicy = discountPolicy;
this.images = images;
this.productName = productName;
this.description = description;
this.originalPrice = originalPrice;
this.mealCategory = mealCategory;
this.bestCategory = bestCategory;
this.fixedPrice = calculateFixedPrice();
}

private long calculateFixedPrice() {
return discountPolicy.calculateFixedPrice(originalPrice);
}
}
@@ -0,0 +1,9 @@
package sidedish.com.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NoSuchProductsException extends RuntimeException {

}
@@ -0,0 +1,12 @@
package sidedish.com.repository;

import java.util.List;
import java.util.Map;
import org.springframework.data.repository.CrudRepository;
import sidedish.com.repository.entity.DiscountPolicyEntity;

public interface DiscountPolicyRepository extends CrudRepository<DiscountPolicyEntity, Long> {

@Override
List<DiscountPolicyEntity> findAll();
}
69 changes: 69 additions & 0 deletions BE/src/main/java/sidedish/com/repository/DomainEntityMapper.java
@@ -0,0 +1,69 @@
package sidedish.com.repository;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
import sidedish.com.domain.DiscountPolicy;
import sidedish.com.domain.Image;
import sidedish.com.domain.Product;
import sidedish.com.repository.entity.DiscountPolicyEntity;
import sidedish.com.repository.entity.ProductEntity;

@Component
public class DomainEntityMapper {

public List<Product> toDomainFromProductsEntity(List<ProductEntity> productEntities,
List<DiscountPolicyEntity> discountPolicies) {
List<Product> products = new ArrayList<>();
for (ProductEntity productEntity : productEntities) {
Long discountPolicyId = productEntity.getDiscountPolicyId();
DiscountPolicyEntity discountPolicyEntity = searchDiscountPolicyById(
discountPolicies, discountPolicyId);

Product product = toDomainFromProductEntity(productEntity, discountPolicyEntity);
products.add(product);
}
return products;
}

private DiscountPolicyEntity searchDiscountPolicyById(
List<DiscountPolicyEntity> discountPolicies, Long discountPolicyId) {
for (DiscountPolicyEntity discountPolicyEntity : discountPolicies) {
if (discountPolicyEntity.isEqualsId(discountPolicyId)) {
return discountPolicyEntity;
}
}
return new DiscountPolicyEntity(null, null, 0);
}

private Product toDomainFromProductEntity(ProductEntity productEntity,
DiscountPolicyEntity discountPolicyEntity) {
DiscountPolicy discountPolicy = toDomainFromDiscountPolicyEntity(discountPolicyEntity);

List<Image> images = toDomainFromImageEntity(productEntity);

return new Product(
productEntity.getId(),
discountPolicy,
images,
productEntity.getProductName(),
productEntity.getDescription(),
productEntity.getOriginalPrice(),
productEntity.getMealCategory(),
productEntity.getBestCategory());
}

private List<Image> toDomainFromImageEntity(ProductEntity productEntity) {
return productEntity.getImageEntities().stream()
.map(imageEntity -> new Image(imageEntity.getImageUrl()))
.collect(Collectors.toList());
}

private DiscountPolicy toDomainFromDiscountPolicyEntity(
DiscountPolicyEntity discountPolicyEntity) {
return new DiscountPolicy(
discountPolicyEntity.getPolicyName(),
discountPolicyEntity.getDiscountRate());
}
}
16 changes: 16 additions & 0 deletions BE/src/main/java/sidedish/com/repository/ProductsRepository.java
@@ -0,0 +1,16 @@
package sidedish.com.repository;

import java.util.List;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import sidedish.com.repository.entity.ProductEntity;

@Repository
public interface ProductsRepository extends CrudRepository<ProductEntity, Long> {

@Query("select id, discount_policy_id, product_name, description, original_price, meal_category "
+ "from PRODUCT where meal_category = :mealType")
List<ProductEntity> findByMealType(@Param("mealType") String mealType);
}
@@ -0,0 +1,27 @@
package sidedish.com.repository.entity;

import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.relational.core.mapping.Table;

@Getter
@Table("DISCOUNT_POLICY")
public class DiscountPolicyEntity {

@Id
private final Long id;
private final String policyName;
private final float discountRate;

@PersistenceConstructor
public DiscountPolicyEntity(Long id, String policyName, float discountRate) {
this.id = id;
this.policyName = policyName;
this.discountRate = discountRate;
}

public boolean isEqualsId(Long id) {
return this.id.equals(id);
}
}
21 changes: 21 additions & 0 deletions BE/src/main/java/sidedish/com/repository/entity/ImageEntity.java
@@ -0,0 +1,21 @@
package sidedish.com.repository.entity;

import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.relational.core.mapping.Table;

@Getter
@Table("PRODUCT_IMAGE")
public class ImageEntity {

@Id
private final Long id;
private final String imageUrl;

@PersistenceConstructor
public ImageEntity(Long id, String imageUrl) {
this.id = id;
this.imageUrl = imageUrl;
}
}
38 changes: 38 additions & 0 deletions BE/src/main/java/sidedish/com/repository/entity/ProductEntity.java
@@ -0,0 +1,38 @@
package sidedish.com.repository.entity;

import java.util.List;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.relational.core.mapping.MappedCollection;
import org.springframework.data.relational.core.mapping.Table;

@Getter
@Table("PRODUCT")
public class ProductEntity {

@Id
private final Long id;
private final Long discountPolicyId;
@MappedCollection(idColumn = "ID", keyColumn = "ID")
private final List<ImageEntity> imageEntities;
private final String productName;
private final String description;
private final long originalPrice;
private final String mealCategory;
private final String bestCategory;

@PersistenceConstructor
public ProductEntity(Long id, Long discountPolicyId,
List<ImageEntity> imageEntities, String productName, String description, long originalPrice,
String mealCategory, String bestCategory) {
this.id = id;
this.discountPolicyId = discountPolicyId;
this.imageEntities = imageEntities;
this.productName = productName;
this.description = description;
this.originalPrice = originalPrice;
this.mealCategory = mealCategory;
this.bestCategory = bestCategory;
}
}
46 changes: 46 additions & 0 deletions BE/src/main/java/sidedish/com/service/ProductsService.java
@@ -0,0 +1,46 @@
package sidedish.com.service;

import java.util.List;
import org.springframework.stereotype.Service;
import sidedish.com.controller.ProductsDtoMapper;
import sidedish.com.controller.model.ProductMealTypeResponse;
import sidedish.com.domain.Product;
import sidedish.com.exception.NoSuchProductsException;
import sidedish.com.repository.DiscountPolicyRepository;
import sidedish.com.repository.DomainEntityMapper;
import sidedish.com.repository.ProductsRepository;

@Service
public class ProductsService {

private final ProductsRepository productsRepository;
private final DiscountPolicyRepository discountPolicyRepository;
private final ProductsDtoMapper productsDtoMapper;
private final DomainEntityMapper domainEntityMapper;

public ProductsService(ProductsRepository productsRepository,
DiscountPolicyRepository discountPolicyRepository,
ProductsDtoMapper productsDtoMapper,
DomainEntityMapper domainEntityMapper) {
this.productsRepository = productsRepository;
this.discountPolicyRepository = discountPolicyRepository;
this.productsDtoMapper = productsDtoMapper;
this.domainEntityMapper = domainEntityMapper;
}

public List<ProductMealTypeResponse> findByMealType(String meal) {
List<Product> products = domainEntityMapper.toDomainFromProductsEntity(
productsRepository.findByMealType(meal),
discountPolicyRepository.findAll());

validProducts(products);

return productsDtoMapper.toProductsMealTypeResponseFromDomain(products);
}

private void validProducts(List<Product> products) {
if (products.isEmpty()) {
throw new NoSuchProductsException();
}
}
}

0 comments on commit eff924c

Please sign in to comment.