Skip to content

Commit

Permalink
v1.3.0 (#828)
Browse files Browse the repository at this point in the history
v1.3.0
  • Loading branch information
Leejin-Yang authored Oct 19, 2023
2 parents 120e4f9 + 25efbfe commit 11e006b
Show file tree
Hide file tree
Showing 254 changed files with 8,404 additions and 2,434 deletions.
114 changes: 103 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,107 @@
# 2023-fun-eat
<div align="center">

## 팀원 👨‍👨‍👧‍👧👩‍👦‍👦
<br>

| Frontend | Frontend | Frontend |
|:-------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|
| <img src="https://avatars.githubusercontent.com/u/55427367?v=4" width=130px alt="타미"> | <img src="https://avatars.githubusercontent.com/u/80464961?v=4" width=130px alt="해온"> | <img src="https://avatars.githubusercontent.com/u/78616893?v=4" width=130px alt="황펭"> |
| [타미](https://github.com/xodms0309) | [해온](https://github.com/hae-on) | [황펭](https://github.com/Leejin-Yang) |
<img src="https://github.com/woowacourse-teams/2023-fun-eat/assets/80464961/85396306-1d3e-4d8e-8763-0e28e2a8be04" width="520px" />

| Backend | Backend | Backend | Backend |
|:--------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|
| <img src="https://avatars.githubusercontent.com/u/79046106?v=4" width=130px alt="로건"/> | <img src="https://avatars.githubusercontent.com/u/33208246?v=4" width=130px alt="망고"/> | <img src="https://avatars.githubusercontent.com/u/91522259?v=4" width=130px alt="오잉"/> | <img src="https://avatars.githubusercontent.com/u/91244090?v=4" width=130px alt="우가"> |
| [로건](https://github.com/70825) | [망고](https://github.com/Go-Jaecheol) | [오잉](https://github.com/hanueleee) | [우가](https://github.com/wugawuga) |
<br>
<br>

<br><br><br>
<b>궁금해? 맛있을걸? 먹어봐! <br>
🍙 편의점 음식 리뷰 & 꿀조합 공유 서비스 🍙</b>

<br>

[![Application](http://img.shields.io/badge/funeat.site-D8EAFF?style=for-the-badge&logo=aHR0cHM6Ly9naXRodWIuY29tL3dvb3dhY291cnNlLXRlYW1zLzIwMjMtZnVuLWVhdC9hc3NldHMvODA0NjQ5NjEvOWI1OWY3NzktY2M5MS00MTJhLWE3NDUtZGQ3M2IzY2UxZGNk&logoColor=black&link=https://funeat.site/)](https://funeat.site/)
[![WIKI](http://img.shields.io/badge/-GitHub%20WiKi-FFEC99?style=for-the-badge&logoColor=black&link=https://github.com/woowacourse-teams/2023-fun-eat/wiki)](https://github.com/woowacourse-teams/2023-fun-eat/wiki)
[![Release](https://img.shields.io/github/v/release/woowacourse-teams/2023-fun-eat?style=for-the-badge&color=FFCFCF)](https://github.com/woowacourse-teams/2023-fun-eat/releases/tag/v1.3.0)

</div>

<br>

# 🥄 서비스 소개

![1_메인페이지](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/9663f7b5-cd38-4f06-86fb-c6636fc364c6)

<br>

## 1. 편의점마다 특색있는 음식 궁금해?

![5_상품목록](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/03fb9955-61fa-4228-a270-ce9dffc710c6)
![6_상품상세](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/694bc8db-74bd-4fa1-b499-900cd27f5028)
![4_검색](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/6a157e08-79d8-450b-9511-ffa461000a22)

<br>
<br>

## 2. 솔직한 리뷰를 보면 더 맛있을걸?

![2_리뷰](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/4bf5ecd7-df08-45d0-b592-8629f3a4e3e6)

<br>
<br>

## 3. 생각지 못했던 꿀조합, 먹어봐!

![3_꿀조합](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/8e560b40-d039-47ce-ad29-5e244cba4bf2)

<br>
<br>

# 🛠️ 기술 스택

### 백엔드

<div align="center">
<img src='https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/5b60393a-ffbf-4595-bb4c-166d091a7998' width="400px" alt="BE_기술스택"/>
</div>

<br/>

### 프론트엔드

<div align="center">
<img src='https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/e3d76698-aaa4-4eea-a878-8c03f3faf395' width="400px" alt="FE_기술스택"/>
</div>

<br/>

### 인프라

<div align="center">
<img src='https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/79399085-1245-4af4-be20-2d5402d53da7' width="400px" alt="인프라_기술스택"/>
</div>

<br>
<br>

# 인프라 구조

### CI/CD

<div align="center">
<img src="https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/3fbef028-d216-4abe-ab4f-c531b099dd33" alt="cicd">
</div>

### 구조

<div align="center">
<img src="https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/3bbb9d40-f525-43ab-8ec2-ade6e6a07139" alt="인프라 구조" />
</div>

<br>
<br>

# 👨‍👨‍👧‍👧👩‍👦‍👦 팀원

| Frontend | Frontend | Frontend | Backend | Backend | Backend | Backend |
| :-------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: |
| <img src="https://avatars.githubusercontent.com/u/55427367?v=4" width=200px alt="타미"> | <img src="https://avatars.githubusercontent.com/u/80464961?v=4" width=200px alt="해온"> | <img src="https://avatars.githubusercontent.com/u/78616893?v=4" width=200px alt="황펭"> | <img src="https://avatars.githubusercontent.com/u/79046106?v=4" width=200px alt="로건"/> | <img src="https://avatars.githubusercontent.com/u/33208246?v=4" width=200px alt="망고"/> | <img src="https://avatars.githubusercontent.com/u/91522259?v=4" width=200px alt="오잉"/> | <img src="https://avatars.githubusercontent.com/u/91244090?v=4" width=200px alt="우가"/> |
| [🐰 타미](https://github.com/xodms0309) | [🌞 해온](https://github.com/hae-on) | [🐧 황펭](https://github.com/Leejin-Yang) | [😺 로건](https://github.com/70825) | [🥭 망고](https://github.com/Go-Jaecheol) | [👻 오잉](https://github.com/hanueleee) | [🍖 우가](https://github.com/wugawuga) |

<br>

<div align="center">
<img src="https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/27ba38de-34b4-4925-a554-9bed89089984" alt="팀소개"/>
</div>
2 changes: 2 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ dependencies {
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'

implementation 'com.amazonaws:aws-java-sdk-s3:1.12.547'

implementation 'org.springframework.session:spring-session-jdbc'
}

tasks.named('test') {
Expand Down
6 changes: 5 additions & 1 deletion backend/src/main/java/com/funeat/FuneatApplication.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.funeat;

import com.funeat.common.repository.BaseRepositoryImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableAsync
@SpringBootApplication
@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryImpl.class)
public class FuneatApplication {

public static void main(String[] args) {
SpringApplication.run(FuneatApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.funeat.admin.application;

import com.funeat.admin.domain.AdminAuthInfo;
import com.funeat.auth.exception.AuthErrorCode;
import com.funeat.auth.exception.AuthException.NotLoggedInException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class AdminChecker {

@Value("${back-office.id}")
private String id;

@Value("${back-office.key}")
private String key;

public boolean check(final AdminAuthInfo adminAuthInfo) {
if (!id.equals(adminAuthInfo.getId())) {
throw new NotLoggedInException(AuthErrorCode.LOGIN_ADMIN_NOT_FOUND);
}

if (!key.equals(adminAuthInfo.getKey())) {
throw new NotLoggedInException(AuthErrorCode.LOGIN_ADMIN_NOT_FOUND);
}

return true;
}
}
117 changes: 117 additions & 0 deletions backend/src/main/java/com/funeat/admin/application/AdminService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.funeat.admin.application;

import static com.funeat.product.exception.CategoryErrorCode.CATEGORY_NOT_FOUND;
import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND;

import com.funeat.admin.dto.AdminProductResponse;
import com.funeat.admin.dto.AdminProductSearchResponse;
import com.funeat.admin.dto.AdminReviewResponse;
import com.funeat.admin.dto.AdminReviewSearchResponse;
import com.funeat.admin.dto.ProductCreateRequest;
import com.funeat.admin.dto.ProductSearchCondition;
import com.funeat.admin.dto.ProductUpdateRequest;
import com.funeat.admin.dto.ReviewSearchCondition;
import com.funeat.admin.repository.AdminProductRepository;
import com.funeat.admin.repository.AdminReviewRepository;
import com.funeat.admin.specification.AdminProductSpecification;
import com.funeat.admin.specification.AdminReviewSpecification;
import com.funeat.product.domain.Category;
import com.funeat.product.domain.Product;
import com.funeat.product.dto.CategoryResponse;
import com.funeat.product.exception.CategoryException.CategoryNotFoundException;
import com.funeat.product.exception.ProductException.ProductNotFoundException;
import com.funeat.product.persistence.CategoryRepository;
import com.funeat.product.persistence.ProductRepository;
import com.funeat.review.domain.Review;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
public class AdminService {

private static final int DEFAULT_PAGE_SIZE = 10;

private final ProductRepository productRepository;
private final AdminProductRepository adminProductRepository;
private final CategoryRepository categoryRepository;
private final AdminReviewRepository adminReviewRepository;

public AdminService(final ProductRepository productRepository, final AdminProductRepository adminProductRepository,
final CategoryRepository categoryRepository, final AdminReviewRepository adminReviewRepository) {
this.productRepository = productRepository;
this.adminProductRepository = adminProductRepository;
this.categoryRepository = categoryRepository;
this.adminReviewRepository = adminReviewRepository;
}

@Transactional
public Long addProduct(final ProductCreateRequest request) {
final Category findCategory = categoryRepository.findById(request.getCategoryId())
.orElseThrow(() -> new CategoryNotFoundException(CATEGORY_NOT_FOUND, request.getCategoryId()));

final Product product = Product.create(request.getName(), request.getPrice(), request.getContent(),
findCategory);

return productRepository.save(product).getId();
}

public List<CategoryResponse> getAllCategories() {
final List<Category> findCategories = categoryRepository.findAll();

return findCategories.stream()
.map(CategoryResponse::toResponse)
.collect(Collectors.toList());
}

public AdminProductSearchResponse getSearchProducts(final ProductSearchCondition condition,
final Pageable pageable) {
final Specification<Product> specification = AdminProductSpecification.searchBy(condition);

final Page<Product> findProducts = adminProductRepository.findAllForPagination(specification, pageable,
condition.getTotalElements());

final List<AdminProductResponse> productResponses = findProducts.stream()
.map(AdminProductResponse::toResponse)
.collect(Collectors.toList());

final Boolean isLastPage = isLastPage(findProducts, condition.getPrePage());

return new AdminProductSearchResponse(productResponses, findProducts.getTotalElements(), isLastPage);
}

private <T> boolean isLastPage(final Page<T> findProducts, Long prePage) {
return prePage * DEFAULT_PAGE_SIZE + findProducts.getContent().size() == findProducts.getTotalElements();
}

@Transactional
public void updateProduct(final Long productId, final ProductUpdateRequest request) {
final Product findProduct = productRepository.findById(productId)
.orElseThrow(() -> new ProductNotFoundException(PRODUCT_NOT_FOUND, productId));

final Category findCategory = categoryRepository.findById(request.getCategoryId())
.orElseThrow(() -> new CategoryNotFoundException(CATEGORY_NOT_FOUND, request.getCategoryId()));

findProduct.update(request.getName(), request.getContent(), request.getPrice(), findCategory);
}

public AdminReviewSearchResponse getSearchReviews(final ReviewSearchCondition condition, final Pageable pageable) {
final Specification<Review> specification = AdminReviewSpecification.searchBy(condition);

final Page<Review> findReviews = adminReviewRepository.findAllForPagination(specification, pageable,
condition.getTotalElements());

final List<AdminReviewResponse> reviewResponses = findReviews.stream()
.map(AdminReviewResponse::toResponse)
.collect(Collectors.toList());

final Boolean isLastPage = isLastPage(findReviews, condition.getPrePage());

return new AdminReviewSearchResponse(reviewResponses, findReviews.getTotalElements(), isLastPage);
}
}
20 changes: 20 additions & 0 deletions backend/src/main/java/com/funeat/admin/domain/AdminAuthInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.funeat.admin.domain;

public class AdminAuthInfo {

private final String id;
private final String key;

public AdminAuthInfo(final String id, final String key) {
this.id = id;
this.key = key;
}

public String getId() {
return id;
}

public String getKey() {
return key;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.funeat.admin.dto;

import com.funeat.product.domain.Category;

public class AdminCategoryResponse {

private final Long id;
private final String name;
private final String image;

public AdminCategoryResponse(final Long id, final String name, final String image) {
this.id = id;
this.name = name;
this.image = image;
}

public static AdminCategoryResponse toResponse(final Category category) {
return new AdminCategoryResponse(category.getId(), category.getName(), category.getImage());
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getImage() {
return image;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.funeat.admin.dto;

import com.funeat.product.domain.Product;

public class AdminProductResponse {

private final Long id;
private final String name;
private final String content;
private final Long price;
private final AdminCategoryResponse categoryResponse;

private AdminProductResponse(final Long id, final String name, final String content,
final Long price, final AdminCategoryResponse categoryResponse) {
this.id = id;
this.name = name;
this.content = content;
this.price = price;
this.categoryResponse = categoryResponse;
}

public static AdminProductResponse toResponse(final Product product) {
final AdminCategoryResponse categoryResponse = AdminCategoryResponse.toResponse(product.getCategory());

return new AdminProductResponse(product.getId(), product.getName(), product.getContent(), product.getPrice(),
categoryResponse);
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getContent() {
return content;
}

public Long getPrice() {
return price;
}

public AdminCategoryResponse getCategoryResponse() {
return categoryResponse;
}
}
Loading

0 comments on commit 11e006b

Please sign in to comment.