Skip to content

Commit

Permalink
Merge pull request #28 from MarketPlace-O2O-Platform/feat/#27
Browse files Browse the repository at this point in the history
[Feat/#27] 이미지 테이블 경로 단일 컬럼으로 수정, 이미지 업로드 기능 구현 및 리소스 핸들러 설정 추가
  • Loading branch information
kangwook1 authored Oct 29, 2024
2 parents eb27159 + 884cc4f commit 9697454
Show file tree
Hide file tree
Showing 21 changed files with 294 additions and 39 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
DOCKER_REPO=marketplace-platform
DOCKER_USER=82everywin
MYSQL_ROOT_PASSWORD=marketplace
MYSQL_DATABASE=marketplace
MYSQL_USER=marketplace
Expand Down
37 changes: 34 additions & 3 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

# .gitignore에 있는 properties파일 추가
- name: Add prod_properties
run: |
mkdir ./src/main/resources
touch ./src/main/resources/application.properties
echo "${{ secrets.PROPERTIES }}" > ./src/main/resources/application.properties
# .gitingnore에 있는 .env파일 docker compose 실행을 위해 추가
- name: Add .env
run: echo "${{ secrets.ENV }}" > .env

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
Expand Down Expand Up @@ -48,11 +59,31 @@ jobs:
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPONAME }}

CD:
needs: [CI]
needs: [ CI ]

runs-on: ubuntu-latest

steps:
- name: Deploy .env to EC2
uses: appleboy/scp-action@master
with:
host: ${{ secrets.DEPLOYMENT_HOST }}
username: ${{ secrets.DEPLOYMENT_USERNAME }}
password: ${{ secrets.DEPLOYMENT_PASSWORD }}
key: ${{ secrets.SSH_KEY }}
source: .env
target: /home/serverking/marketplace

- name: Deploy docker compose file
uses: appleboy/scp-action@master
with:
host: ${{ secrets.DEPLOYMENT_HOST }}
username: ${{ secrets.DEPLOYMENT_USERNAME }}
password: ${{ secrets.DEPLOYMENT_PASSWORD }}
key: ${{ secrets.SSH_KEY }}
source: docker-compose.yml
target: /home/serverking/marketplace

- name: Deploy remote ssh commands using password
uses: appleboy/ssh-action@master
with:
Expand All @@ -63,5 +94,5 @@ jobs:
script_stop: true
script: |
cd marketplace
chmod +x ./deploy.sh
./deploy.sh
docker-compose pull
docker-compose up -d
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
application-dev.properties
application-prod.properties
.env
.idea
*.iws
*.iml
Expand Down
26 changes: 9 additions & 17 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
version: '3.8'

services:
mysql:
image: mysql:latest
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql


volumes:
mysql_data:

springboot:
container_name: marketplace
image: ${DOCKER_USER}/${DOCKER_REPO}:marketplace-server # Docker Hub에서 가져올 이미지
volumes:
- /home/serverking/marketplace:/app/image
ports:
- "8088:8080"
environment:
TZ: "Asia/Seoul"
20 changes: 20 additions & 0 deletions local/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: '3.8'

services:
mysql:
image: mysql:latest
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql


volumes:
mysql_data:

16 changes: 8 additions & 8 deletions src/main/java/com/appcenter/marketplace/domain/image/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import com.appcenter.marketplace.domain.market.Market;
import com.appcenter.marketplace.global.common.BaseEntity;
import jakarta.annotation.Nullable;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -18,15 +18,15 @@ public class Image extends BaseEntity {
private Long id;

@Column(nullable = false)
private String uuid;

@Column(nullable = false)
private String folder;

@Column(name = "original_name", nullable = false)
private String originalName;
private String name;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn( name = "market_id", nullable = false)
private Market market;

@Builder
public Image(String name, Market market) {
this.name=name;
this.market = market;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.appcenter.marketplace.domain.image;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ImageRepository extends JpaRepository<Image,Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.appcenter.marketplace.domain.image.service;

import com.appcenter.marketplace.domain.market.Market;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

public interface ImageService {

void createImage(Market market, List<MultipartFile> multipartFileList) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.appcenter.marketplace.domain.image.service.impl;

import com.appcenter.marketplace.domain.image.Image;
import com.appcenter.marketplace.domain.image.ImageRepository;
import com.appcenter.marketplace.domain.image.service.ImageService;
import com.appcenter.marketplace.domain.market.Market;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

@Transactional(readOnly = true)
@Service
@RequiredArgsConstructor
public class ImageServiceImpl implements ImageService {
private final ImageRepository imageRepository;

@Value("${image.upload.path}")
private String uploadFolder;

// 이미지 리스트의 첫번째 요소를 썸네일로 설정
@Override
@Transactional
public void createImage(Market market, List<MultipartFile> multipartFileList) throws IOException {

for (int i = 0; i < multipartFileList.size(); i++){
MultipartFile file = multipartFileList.get(i);
String imageFileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
File uploadFile = new File(uploadFolder + imageFileName);

file.transferTo(uploadFile);

Image image= Image.builder()
.name(imageFileName)
.market(market)
.build();
imageRepository.save(image);

if(i==0){
market.updateThumbnailPath(image.getName());
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,7 @@ public void updateMarketInfo(MarketUpdateReqDto marketUpdateReqDto, Category cat
this.address = marketUpdateReqDto.getAddress();
this.category= category;
}
public void updateThumbnailPath(String thumbnail){
this.thumbnail= thumbnail;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

import static com.appcenter.marketplace.global.common.StatusCode.*;

Expand All @@ -19,12 +24,15 @@
public class MarketOwnerController {
private final MarketOwnerService marketOwnerService;

@Operation(summary = "사장님 매장 생성", description = "사장님이 1개의 매장을 생성합니다.")
@PostMapping
public ResponseEntity<CommonResponse<MarketResDto>> createMarket(@RequestBody @Valid MarketCreateReqDto marketCreateReqDto){
@Operation(summary = "사장님 매장 생성", description = "사장님이 1개의 매장을 생성합니다. <br>" +
"이미지를 가져오려면 /image/{image.name}을 fetch하면 됩니다.")
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<CommonResponse<MarketResDto>> createMarket(
@RequestPart(value = "jsonData") @Valid MarketCreateReqDto marketCreateReqDto,
@RequestPart(value = "files") List<MultipartFile> multipartFileList) throws IOException {
return ResponseEntity
.status(MARKET_CREATE.getStatus())
.body(CommonResponse.from(MARKET_CREATE.getMessage(),marketOwnerService.createMarket(marketCreateReqDto)));
.body(CommonResponse.from(MARKET_CREATE.getMessage(),marketOwnerService.createMarket(marketCreateReqDto,multipartFileList)));
}

@Operation(summary = "사장님 매장 정보 수정", description = "사장님이 생성한 매장 정보를 수정합니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
import com.appcenter.marketplace.domain.market.dto.req.MarketCreateReqDto;
import com.appcenter.marketplace.domain.market.dto.req.MarketUpdateReqDto;
import com.appcenter.marketplace.domain.market.dto.res.MarketResDto;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

public interface MarketOwnerService {

MarketResDto createMarket(MarketCreateReqDto marketCreateReqDto);
MarketResDto createMarket(MarketCreateReqDto marketCreateReqDto, List<MultipartFile> multiPartFileList) throws IOException;

MarketResDto updateMarket(MarketUpdateReqDto marketUpdateReqDto, Long marketId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.appcenter.marketplace.domain.category.Category;
import com.appcenter.marketplace.domain.category.CategoryRepository;
import com.appcenter.marketplace.domain.image.service.ImageService;
import com.appcenter.marketplace.domain.market.Market;
import com.appcenter.marketplace.domain.market.MarketRepository;
import com.appcenter.marketplace.domain.market.dto.req.MarketCreateReqDto;
Expand All @@ -13,22 +14,29 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import static com.appcenter.marketplace.global.common.StatusCode.*;
import java.io.IOException;
import java.util.List;

import static com.appcenter.marketplace.global.common.StatusCode.CATEGORY_NOT_EXIST;
import static com.appcenter.marketplace.global.common.StatusCode.MARKET_NOT_EXIST;

@Transactional(readOnly = true)
@Service
@RequiredArgsConstructor
public class MarketOwnerServiceImpl implements MarketOwnerService {
private final MarketRepository marketRepository;
private final CategoryRepository categoryRepository;
private final ImageService imageService;


@Override
@Transactional
public MarketResDto createMarket(MarketCreateReqDto marketCreateReqDto) {
public MarketResDto createMarket(MarketCreateReqDto marketCreateReqDto, List<MultipartFile> multipartFileList) throws IOException {
Category category=findCategoryByMajor(marketCreateReqDto.getMajor());
Market market=marketRepository.save(marketCreateReqDto.toEntity(category));
imageService.createImage(market,multipartFileList);
return MarketResDto.from(market);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
import com.appcenter.marketplace.domain.member.dto.req.MemberLoginReqDto;
import com.appcenter.marketplace.domain.member.dto.res.MemberLoginResDto;
import com.appcenter.marketplace.domain.member.service.MemberService;
import static com.appcenter.marketplace.global.common.StatusCode.*;
import com.appcenter.marketplace.global.exception.CustomException;
import com.appcenter.marketplace.global.oracleRepository.InuLoginRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.appcenter.marketplace.global.common.StatusCode.INVALID_STUDENT_ID;
import static com.appcenter.marketplace.global.common.StatusCode.UNAUTHORIZED_LOGIN_ERROR;

@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
Expand All @@ -33,5 +35,6 @@ private Long validateAndParseStudentId(MemberLoginReqDto memberLoginReqDto) {
if(inuLoginRepository.loginCheck(memberLoginReqDto.getStudentId(), memberLoginReqDto.getPassword()))
return Long.valueOf(memberLoginReqDto.getStudentId());
else throw new CustomException(UNAUTHORIZED_LOGIN_ERROR);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public enum StatusCode {
/* 400 BAD_REQUEST : 잘못된 요청 */
INPUT_VALUE_INVALID(BAD_REQUEST,"유효하지 않은 입력입니다."),
INVALID_STUDENT_ID(BAD_REQUEST,"유효하지 않은 학번입니다."),
MULTI_PART_FILE_INVALID(BAD_REQUEST,"유효하지 않은 MultiPartFile입니다."),
FILE_INVALID(BAD_REQUEST,"파일 저장 중 오류가 발생했습니다."),

/* 401 UNAUTHORIZED : 비인증 사용자 */
UNAUTHORIZED_LOGIN_ERROR(UNAUTHORIZED, "학번 또는 비밀번호가 올바르지 않습니다."),
Expand Down
Loading

0 comments on commit 9697454

Please sign in to comment.