Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/weekly/11' into feat/#142-mypage
Browse files Browse the repository at this point in the history
  • Loading branch information
wndlthsk committed Nov 14, 2024
2 parents 27f38c3 + ad92043 commit 2426660
Show file tree
Hide file tree
Showing 32 changed files with 1,142 additions and 302 deletions.
150 changes: 119 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,166 @@
# Project : Inplace ( Team7_BE )
# Inplace ( Team7_BE )

## 배포 링크
> [**BackEnd**](https://api.inplace.my) : _api.inplace.my_
>
> [**FrontEnd**](https://inplace.my) : _inplace.my_
<p align="center">
<img src="https://i.ibb.co/CVBQHPg/image-2.png" alt="메인 이미지" style="border: 2px solid black;">
</p>

## 개발 Repository 정보
## Project Purpose
> 저희의 아이디어는 **데이트 코스의 단조로움**을 어떻게 하면 해소할 수 있을까? 라는 생각에서 시작했습니다.
>
> 아이디어를 구체화하는 과정에서 저희는 **장소**에 관련된 소재로 인스타, 유튜브 등의 SNS 및 동영상 플랫폼의 성장과 함께 등장한 **인플루언서** 라는 개념에 집중하게 되었습니다.
>
> 이는 **인플루언서가 방문한 장소에 대한 정보를 서비스 해보자!** 라는 생각으로 이어졌습니다.
>
> 이를 관심있는 **인플루언서를 등록하고, 이에 따른 장소 추천 및, 장소에 대한 리뷰 기능**을 통해 풀어내어 **Inplace** 라는 저희만의 웹 애플리케이션으로 구현해보았습니다.
## Main Function
> 프로그램 시나리오
- 사용자 회원가입
- 사용자 로그인
- 관심있는 인플루언서 선택 ( 초기 한정 )
- 사용자 별 맞춤 페이지 제공 ( 좋아요 한 인플루언서의 영상, 내 주변 인플루언서 방문 장소에 대한 영상 )
- 인플루언서, 장소, 동영상 통합 검색 기능
- 인플루언서 검색 기능
- 인플루언서 좋아요 기능
- 지도를 통한 장소 검색 기능 ( 위치 정보, 인플루언서 정보, 장소 태그 별 검색 )
- 장소에 대한 상세 페이지 제공 기능 ( 장소 정보, 리뷰 정보 )
- 방문 예정 기능 ( 나에게 카카오 메세지 전송 )
- 방문 예정 이후 3일 뒤, 리뷰 기능 ( 나에게 카카오 메세지 전송 )
- 마이페이지 기능 ( 유저 별 좋아요 한 인플루언서, 장소와 리뷰 조회, 닉네임 변경 가능 )

## Repository Info
![GitHub language count](https://img.shields.io/github/languages/count/kakao-tech-campus-2nd-step3/Team7_BE)
![GitHub top language](https://img.shields.io/github/languages/top/kakao-tech-campus-2nd-step3/Team7_BE)
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/kakao-tech-campus-2nd-step3/Team7_BE)
![GitHub repo size](https://img.shields.io/github/repo-size/kakao-tech-campus-2nd-step3/Team7_BE)
![GitHub open issues](https://img.shields.io/github/issues/kakao-tech-campus-2nd-step3/Team7_BE)
![GitHub closed issues](https://img.shields.io/github/issues-closed/kakao-tech-campus-2nd-step3/Team7_BE)
![GitHub commit activity](https://img.shields.io/github/commit-activity/w/kakao-tech-campus-2nd-step3/Team7_BE)
![GitHub last commit](https://img.shields.io/github/last-commit/kakao-tech-campus-2nd-step3/Team7_BE)

## 참여자
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
## Deploy Link
> [**BackEnd**](https://api.inplace.my) : _api.inplace.my_
>
> [**API Spec**](https://api.inplace.my/swagger-ui/index.html) : _api.inplace.my/swagger-ui/index.html_
>
> [**FrontEnd**](https://inplace.my) : _inplace.my_
## Contributor
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<table>
<tr>
<td align="center"><a href="https://github.com/sanghee0820"><img src="https://avatars.githubusercontent.com/u/102018082?v=4" width="100px;" alt=""/><br /><sub><b>이상희</b></sub></a><br /><a href="https://github.com/sanghee0820" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/dong-yxxn"><img src="https://avatars.githubusercontent.com/u/129285999?v=4" width="100px;" alt=""/><br /><sub><b>김동윤</b></sub></a><br /><a href="https://github.com/dong-yxxn" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/suhyeon7497"><img src="https://avatars.githubusercontent.com/u/137245467?v=4" width="100px;" alt=""/><br /><sub><b>정수현</b></sub></a><br /><a href="https://github.com/suhyeon7497" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/wndlthsk"><img src="https://avatars.githubusercontent.com/u/80496766?v=4" width="100px;" alt=""/><br /><sub><b>우현서</b></sub></a><br /><a href="https://github.com/wndlthsk" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/BaeJunH0"><img src="https://avatars.githubusercontent.com/u/114082026?v=4" width="100px;" alt=""/><br /><sub><b>배준호</b></sub></a><br /><a href="https://github.com/BaeJunH0" title="Code">💻</a></td>
<!-- Add more contributors here -->
<td align="center">
<b>Frontend</b><br />
</td>
<td align="center">
<b>Frontend</b><br />
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/userjmmm"><img src="https://avatars.githubusercontent.com/u/141299582?v=4" width="80px;" alt=""/><br /><sub><b>이정민</b></sub></a>
</td>
<td align="center">
<a href="https://github.com/Hyoeunkh"><img src="https://avatars.githubusercontent.com/u/102338613?v=4" width="80px;" alt=""/><br /><sub><b>이효은</b></sub></a>
</td>
</tr>
</table>
<table>
<tr>
<td align="center">
<b>Backend</b><br />
</td>
<td align="center">
<b>Backend</b><br />
</td>
<td align="center">
<b>Backend</b><br />
</td>
<td align="center">
<b>Backend</b><br />
</td>
<td align="center">
<b>Backend</b><br />
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/sanghee0820"><img src="https://avatars.githubusercontent.com/u/102018082?v=4" width="80px;" alt=""/><br /><sub><b>이상희</b></sub></a>
</td>
<td align="center">
<a href="https://github.com/dong-yxxn"><img src="https://avatars.githubusercontent.com/u/129285999?v=4" width="80px;" alt=""/><br /><sub><b>김동윤</b></sub></a>
</td>
<td align="center">
<a href="https://github.com/suhyeon7497"><img src="https://avatars.githubusercontent.com/u/137245467?v=4" width="80px;" alt=""/><br /><sub><b>정수현</b></sub></a>
</td>
<td align="center">
<a href="https://github.com/wndlthsk"><img src="https://avatars.githubusercontent.com/u/80496766?v=4" width="80px;" alt=""/><br /><sub><b>우현서</b></sub></a>
</td>
<td align="center">
<a href="https://github.com/BaeJunH0"><img src="https://avatars.githubusercontent.com/u/114082026?v=4" width="80px;" alt=""/><br /><sub><b>배준호</b></sub></a>
</td>
</tr>
</table>
<!-- ALL-CONTRIBUTORS-LIST:END -->

## 기획 의도
> **데이트 코스의 단조로움** 이라는 생각에서 시작한 저희의 아이디어는 인스타, 유튜브 등의 SNS 플랫폼의 성장과 함께 등장한 '인플루언서' 라는 개념에 집중하여, **인플루언서가 방문한 주변 장소에 대한 정보를 서비스 해보자!** 라는 생각으로 이어졌고, 이를 개인별 맞춤 장소 추천, 방문시 정보 발송 및 알림, 리뷰와 같은 기능으로 풀어내어 **Inplace** 라는 저희만의 웹 어플리케이션으로 구현해보았습니다.
## 주요 기능

## 프로그램 구조
## Program Architecture
- **ERD**

<img src="https://file.notion.so/f/f/3ef8dbd9-414c-4cf5-813d-32ecb943cc67/125d6695-92d4-438c-8bc8-bee423d36257/image.png?table=block&id=8410a24e-b147-4649-9b77-304c9fd6599a&spaceId=3ef8dbd9-414c-4cf5-813d-32ecb943cc67&expirationTimestamp=1731585600000&signature=nfFPKXrEnsF9lTp65eWkTl9Gjhs_r8Yqf2qG4nAAmTw&downloadName=image.png" alt="ERD" width="600"/>

-

## 주요 종속성 버전
- Spring Boots 3.3.3
- Java 17 LTS
## Main Dependency Version
> **Spring Boots 3.3.3**
>
> **Java 17 LTS**
## 기술 스택
## Tech Stack
> **Backend**
>
![Spring Boot](https://img.shields.io/badge/Spring%20Boot-6DB33F?style=flat-square&logo=springboot&logoColor=white)
![Lombok](https://img.shields.io/badge/Lombok-DC382D?style=flat-square&logo=lombok&logoColor=white)

> **Security**
>
![Spring Security](https://img.shields.io/badge/Spring%20Security-6DB33F?style=flat-square&logo=springsecurity&logoColor=white)
![JWT](https://img.shields.io/badge/JWT-000000?style=flat-square&logo=jsonwebtokens&logoColor=white)

> **DB**
>
![Spring Data JPA](https://img.shields.io/badge/Spring%20Data%20JPA-6DB33F?style=flat-square&logo=spring&logoColor=white)
![QueryDSL](https://img.shields.io/badge/QueryDSL-0055a2?style=flat-square&logo=appveyor&logoColor=white)
![MySQL](https://img.shields.io/badge/MySQL-4479A1?style=flat-square&logo=mysql&logoColor=white)
![Redis](https://img.shields.io/badge/Redis-DC382D?style=flat-square&logo=redis&logoColor=white)

> **Web**
>
![Spring WebFlux](https://img.shields.io/badge/Spring%20WebFlux-6DB33F?style=flat-square&logo=spring&logoColor=white)
![Thymeleaf](https://img.shields.io/badge/Thymeleaf-005F0F?style=flat-square&logo=thymeleaf&logoColor=white)

> **Deployment**
>
![AWS EC2](https://img.shields.io/badge/AWS%20EC2-FF9900?style=flat-square&logo=amazonaws&logoColor=white)
![Docker](https://img.shields.io/badge/Docker-2496ED?style=flat-square&logo=docker&logoColor=white)

> **Admin Page**
>
![jQuery](https://img.shields.io/badge/jQuery-0769AD?style=flat-square&logo=jquery&logoColor=white)
![Thymeleaf](https://img.shields.io/badge/Thymeleaf-005F0F?style=flat-square&logo=thymeleaf&logoColor=white)
![AJAX](https://img.shields.io/badge/AJAX-005571?style=flat-square&logo=ajax&logoColor=white)

> **Test**
>
![JMeter](https://img.shields.io/badge/JMeter-D22128?style=flat-square&logo=apachejmeter&logoColor=white)
![JUnit](https://img.shields.io/badge/JUnit-25A162?style=flat-square&logo=junit5&logoColor=white)
![Mockito](https://img.shields.io/badge/Mockito-FFCA28?style=flat-square&logo=mockito&logoColor=white)

> **Code Maintenance**
>
![Git](https://img.shields.io/badge/Git-F05032?style=flat-square&logo=git&logoColor=white)
![GitHub](https://img.shields.io/badge/GitHub-181717?style=flat-square&logo=github&logoColor=white)
![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-2088FF?style=flat-square&logo=githubactions&logoColor=white)

## 사용 예시
> **Collaboration Tool**
>
![Discord](https://img.shields.io/badge/Discord-5865F2?style=flat-square&logo=discord&logoColor=white)
![Slack](https://img.shields.io/badge/Slack-4A154B?style=flat-square&logo=slack&logoColor=white)

## Example Usage
> 나중에 시연 영상 첨부하기
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
//Spring
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
testImplementation 'io.projectreactor:reactor-test'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/team7/inplace/admin/AdminPageController.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import team7.inplace.admin.persistence.BannerRepository;
import team7.inplace.global.exception.ErrorLog;
import team7.inplace.global.exception.ErrorLogRepository;
import team7.inplace.global.kakao.config.KakaoApiProperties;
Expand All @@ -23,6 +24,7 @@ public class AdminPageController {
private final KakaoApiProperties kakaoApiProperties;
private final VideoRepository videoRepository;
private final ErrorLogRepository errorLogRepository;
private final BannerRepository bannerRepository;

@GetMapping("/video")
public String getVideos(@PageableDefault Pageable pageable, Model model) {
Expand All @@ -42,4 +44,12 @@ public String getErrorLogs(Model model) {
model.addAttribute("errorLogs", errorLogs);
return "admin/error-logs.html";
}

@GetMapping("/banner")
public String getBanners(Model model) {
var banners = bannerRepository.findAll();

model.addAttribute("banners", banners);
return "admin/banner.html";
}
}
34 changes: 34 additions & 0 deletions src/main/java/team7/inplace/admin/application/BannerService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package team7.inplace.admin.application;

import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import team7.inplace.admin.application.command.BannerCommand;
import team7.inplace.admin.application.dto.BannerInfo;
import team7.inplace.admin.application.dto.BannerInfo.Detail;
import team7.inplace.admin.persistence.BannerRepository;
import team7.inplace.admin.persistence.BannerS3Repository;

@Service
@RequiredArgsConstructor
public class BannerService {
private final BannerS3Repository bannerS3Repository;
private final BannerRepository bannerRepository;

public void uploadBanner(BannerCommand.Create command) {
var imgPath = bannerS3Repository.uploadBanner(command.imgName(), command.imageFile());
var banner = command.toEntity(imgPath);
bannerRepository.save(banner);
}

public List<Detail> getBanners() {
var now = LocalDateTime.now();
var banners = bannerRepository.findActiveBanner(now);

return banners.stream()
.sorted((a, b) -> Boolean.compare(b.getIsFixed(), a.getIsFixed()))
.map(BannerInfo.Detail::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package team7.inplace.admin.application.command;

import java.time.LocalDateTime;
import org.springframework.web.multipart.MultipartFile;
import team7.inplace.admin.domain.Banner;

public class BannerCommand {
public record Create(
String imgName,
MultipartFile imageFile,
LocalDateTime startDate,
LocalDateTime endDate,
Boolean isFixed
) {
public Banner toEntity(String imgPath) {
return Banner.of(imgName, imgPath, startDate, endDate, isFixed);
}
}
}
14 changes: 14 additions & 0 deletions src/main/java/team7/inplace/admin/application/dto/BannerInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package team7.inplace.admin.application.dto;

import team7.inplace.admin.domain.Banner;

public class BannerInfo {
public record Detail(
Long id,
String imageUrl
) {
public static Detail from(Banner banner) {
return new Detail(banner.getId(), banner.getImgPath());
}
}
}
38 changes: 38 additions & 0 deletions src/main/java/team7/inplace/admin/domain/Banner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package team7.inplace.admin.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Banner {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String imgName;
private String imgPath;
private LocalDateTime startDate;
private LocalDateTime endDate;
private Boolean isFixed;

private Banner(String imgName, String imgPath, LocalDateTime startDate, LocalDateTime endDate, Boolean isFixed) {
this.imgName = imgName;
this.imgPath = imgPath;
this.startDate = startDate;
this.endDate = endDate;
this.isFixed = isFixed;
}

public static Banner of(String imgName, String imgPath, LocalDateTime startDate, LocalDateTime endDate,
Boolean isFixed) {
return new Banner(imgName, imgPath, startDate, endDate, isFixed);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package team7.inplace.admin.persistence;

import java.time.LocalDateTime;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import team7.inplace.admin.domain.Banner;

@Repository
public interface BannerRepository extends JpaRepository<Banner, Long> {
@Query("SELECT l FROM Banner l WHERE l.startDate <= :now AND l.endDate >= :now or l.isFixed = true")
List<Banner> findActiveBanner(@Param("now") LocalDateTime now);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package team7.inplace.admin.persistence;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectMetadata;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.web.multipart.MultipartFile;
import team7.inplace.infra.s3.AwsProperties;

@Repository
@RequiredArgsConstructor
public class BannerS3Repository {
private final AmazonS3Client amazonS3Client;
private final AwsProperties awsProperties;

public String uploadBanner(String bannerName, MultipartFile banner) {
var bucketName = awsProperties.bucketName();
var key = "banner/" + bannerName;

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(banner.getSize());
metadata.setContentType(banner.getContentType());

try {
amazonS3Client.putObject(bucketName, key, banner.getInputStream(), metadata);
return amazonS3Client.getUrl(bucketName, key).toString();
} catch (Exception e) {
throw new RuntimeException("Failed to upload banner", e);
}
}
}
Loading

0 comments on commit 2426660

Please sign in to comment.