Skip to content
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: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
//업로드
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}

tasks.named('test') {
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/com/example/aws/S3Manager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.example.aws;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.example.config.AmazonConfig;
import com.example.domain.Uuid;
import com.example.repository.UuidRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@Slf4j
@Component
@RequiredArgsConstructor
public class S3Manager {
private final AmazonS3 amazonS3;
private final AmazonConfig awsConfig;
private final UuidRepository uuidRepository;

public String uploadFile(String keyName, MultipartFile file){
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(file.getSize());
try {
amazonS3.putObject(new PutObjectRequest(awsConfig.getBucket(), keyName, file.getInputStream(), metadata));
} catch (IOException e){
log.error("error at AmazonS3Manager uploadFile : {}", (Object) e.getStackTrace());
}

return amazonS3.getUrl(awsConfig.getBucket(), keyName).toString();}

public String generateReviewKeyName(Uuid uuid) {

return awsConfig.getReviewPath() + '/' + uuid.getUuid();
}
}
54 changes: 54 additions & 0 deletions src/main/java/com/example/config/AmazonConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.example.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

@Configurable
@Getter
public class AmazonConfig {

private AWSCredentials awsCredentials;

@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

@Value("${cloud.aws.region.static}")
private String region;

@Value("${cloud.aws.s3.bucket}")
private String bucket;

@Value("${cloud.aws.s3.path.review}")
private String reviewPath;

@PostConstruct
public void init() {
this.awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
}

@Bean
public AmazonS3 amazonS3() {
AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();
}

@Bean
public AWSCredentialsProvider awsCredentialsProvider() {
return new AWSStaticCredentialsProvider(awsCredentials);
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/example/domain/Uuid.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.domain;

import com.example.domain.base.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Uuid extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true)
private String uuid;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.repository;

import com.example.domain.mapping.ReviewImage;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ReviewImageRepository extends JpaRepository<ReviewImage, Long> {
}
7 changes: 7 additions & 0 deletions src/main/java/com/example/repository/UuidRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.repository;

import com.example.domain.Uuid;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UuidRepository extends JpaRepository<Uuid, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import com.example.domain.Review;
import com.example.web.dto.ReviewRequest;
import org.springframework.web.multipart.MultipartFile;

public interface ReviewCommandService {
Review join(ReviewRequest.JoinDto dto);
Review join(ReviewRequest.JoinDto dto, Long memberId, Long storeId, MultipartFile reviewImage);
// join이 리뷰 생성을 수행한 후 저장된 Review 엔티티를 반환
// DB에서 review 객체를 생성하고 반환하는 역할

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package com.example.service.review;

import com.example.aws.S3Manager;
import com.example.converter.ReviewConverter;
import com.example.domain.Member;
import com.example.domain.Review;
import com.example.domain.Store;
import com.example.domain.Uuid;
import com.example.exception.handler.MemberHandler;
import com.example.exception.handler.StoreHandler;
import com.example.payload.status.ErrorStatus;
import com.example.repository.MemberRepository;
import com.example.repository.ReviewRepository;
import com.example.repository.StoreRepository;
import com.example.repository.UuidRepository;
import com.example.web.dto.ReviewRequest;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.UUID;

@Service
@Transactional
Expand All @@ -22,6 +28,8 @@ public class ReviewCommandServiceImpl implements ReviewCommandService{
private final ReviewRepository reviewRepository;
private final MemberRepository memberRepository;
private final StoreRepository storeRepository;
private final UuidRepository uuidRepository;
private final S3Manager s3Manager;


public boolean memberExistsById(Long memberId) {
Expand All @@ -33,7 +41,7 @@ public boolean storeExistsById(Long storeId) {
}

@Override
public Review join(ReviewRequest.JoinDto dto){ //생성
public Review join(ReviewRequest.JoinDto dto,Long memberId, Long storeId, MultipartFile reviewImage){ //생성

//회원 있는지 확인
Member member = memberRepository.findById(dto.getMemberId()).get();
Expand All @@ -42,6 +50,11 @@ public Review join(ReviewRequest.JoinDto dto){ //생성
Store store = storeRepository.findById(dto.getStoreId()).get();

Review newReview = ReviewConverter.toReview(dto,member,store);
String uuid = UUID.randomUUID().toString();
Uuid savedUuid = uuidRepository.save(Uuid.builder()
.uuid(uuid)
.build());
String imageUrl = s3Manager.uploadFile(s3Manager.generateReviewKeyName(savedUuid), reviewImage);
return reviewRepository.save(newReview);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.service.store;

import com.example.domain.Review;
import com.example.web.dto.ReviewResponse;
import org.springframework.web.multipart.MultipartFile;

public interface StoreCommandService {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.service.store;

import com.example.aws.S3Manager;
import com.example.converter.ReviewConverter;
import com.example.domain.Review;
import com.example.domain.Uuid;
import com.example.repository.ReviewImageRepository;
import com.example.repository.ReviewRepository;
import com.example.repository.StoreRepository;
import com.example.repository.UuidRepository;
import com.example.service.member.MemberQueryService;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.UUID;

@Service
@RequiredArgsConstructor
@Transactional
public class StoreCommandServiceImpl implements StoreCommandService {
}
11 changes: 3 additions & 8 deletions src/main/java/com/example/web/controller/ReviewController.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,9 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("/reviews")
@RequestMapping()
public class ReviewController {
private final ReviewCommandService reviewCommandService;

@PostMapping("/")
@Operation(summary = "가게 리뷰 추가 API")
public CommonResponse<ReviewResponse.JoinResultDto> join(@RequestBody @Valid ReviewRequest.JoinDto dto){
Review review = reviewCommandService.join(dto);
return CommonResponse.onSuccess(ReviewConverter.toJoinResultDto(review));
}


}
29 changes: 29 additions & 0 deletions src/main/java/com/example/web/controller/StoreController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import com.example.domain.Mission;
import com.example.domain.Review;
import com.example.payload.CommonResponse;
import com.example.service.review.ReviewCommandService;
import com.example.service.store.StoreQueryService;
import com.example.validation.annotaion.CheckPage;
import com.example.validation.annotaion.ExistsMember;
import com.example.validation.annotaion.ExistsStore;
import com.example.web.dto.MissionResponse;
import com.example.web.dto.ReviewRequest;
import com.example.web.dto.ReviewResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -17,10 +20,12 @@
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 jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@Validated
@RestController
Expand All @@ -29,6 +34,29 @@
public class StoreController {

private final StoreQueryService storeQueryService;
private final ReviewCommandService reviewCommandService;

@PostMapping("/{storeId}/{memberId}/reviews")
@Operation(summary = "가게 리뷰 추가 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON_200", description = "성공"),
@ApiResponse(responseCode = "STORE_4001", description = "가게를 찾을 수 없습니다.",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
@ApiResponse(responseCode = "Member_4001", description = "사용자를 찾을 수 없습니다.",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@Parameters(value = {
@Parameter(name = "storeId", description = "가게 ID(path variable)", required = true),
@Parameter(name = "memberId", description = "사용자 ID(path variable)", required = true),
})
public CommonResponse<ReviewResponse.JoinResultDto> join(
@ExistsStore @PathVariable(name = "storeId") Long storeId,
@ExistsMember @PathVariable(name = "memberId") Long memberId,
@RequestPart MultipartFile reviewImage,
@RequestBody @Valid ReviewRequest.JoinDto dto){
Review review = reviewCommandService.join(dto, storeId, memberId, reviewImage);
return CommonResponse.onSuccess(ReviewConverter.toJoinResultDto(review));
}

@GetMapping("/{storeId}/reviews")
@Operation(summary = "특정 가게의 리뷰 목록 조회 API",
Expand All @@ -49,6 +77,7 @@ public CommonResponse<ReviewResponse.ReviewListDto> getStoreReviews(
return CommonResponse.onSuccess(ReviewConverter.toReviewListDto(reviews));
}


@GetMapping("/{storeId}/missions")
@Operation(summary = "특정 가게의 미션 목록 조회 API")
@Parameters(value = {
Expand Down
16 changes: 14 additions & 2 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
spring:
application:
name : beanstalk

datasource:
url: ${DATABASE_URL}
Expand All @@ -27,6 +25,20 @@ spring:
max-file-size: 100MB
max-request-size: 300MB

cloud:
aws:
s3:
bucket: web101-bucket
path:
review: reviews
region:
static: ap-northeast-2
stack:
auto: false
credentials:
accessKey: ${S3_SECRET_KEY}
secretKey: ${S3_ACCESS_KEY}

springdoc:
swagger-ui:
path: /swagger
Expand Down