Skip to content

Commit

Permalink
Merge pull request #12 from Ong-gi-Jong-gi/feature/TSK-38/face-challenge
Browse files Browse the repository at this point in the history
🚨GitHub workflow 수정
  • Loading branch information
minjoon-98 authored Jul 9, 2024
2 parents 2bdf388 + 43b9251 commit 9318e71
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 32 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/Spring Boot CI-CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ jobs:
-e SPRING_DATASOURCE_USERNAME="${{ secrets.DB_USERNAME }}" \
-e SPRING_DATASOURCE_PASSWORD="${{ secrets.DB_PASSWORD }}" \
-e SECURITY_JWT_TOKEN_SECRET_KEY="${{ secrets.JWT_SECRET_KEY }}" \
-e AWS_ACCESS_KEY_ID="${{ secrets.S3_ACCESS_KEY_ID }}" \
-e AWS_SECRET_ACCESS_KEY="${{ secrets.S3_SECRET_ACCESS_KEY }}" \
-e S3_ACCESS_KEY_ID="${{ secrets.S3_ACCESS_KEY_ID }}" \
-e S3_SECRET_ACCESS_KEY="${{ secrets.S3_SECRET_ACCESS_KEY }}" \
-e S3_BUCKET_NAME="${{ secrets.S3_BUCKET_NAME }}" \
${{ env.IMAGE_NAME }}
sudo docker ps -a
sudo docker system prune -f
set +x
set +x
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ repositories {
}

dependencies {

// Spring Boot dependencies
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
Expand All @@ -39,6 +40,7 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'


// Other dependencies
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'commons-codec:commons-codec:1.13'
Expand All @@ -48,8 +50,11 @@ dependencies {
// AWS SDK dependencies using BOM
implementation platform('com.amazonaws:aws-java-sdk-bom:1.12.529')
implementation 'com.amazonaws:aws-java-sdk-s3'
// implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
// implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

// Jackson for JSON processing
implementation 'com.fasterxml.jackson.jr:jackson-jr-all:2.15.2'
}

tasks.named('test') {
Expand Down
88 changes: 84 additions & 4 deletions src/main/java/ongjong/namanmoo/controller/ChallengeController.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ongjong.namanmoo.controller;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import ongjong.namanmoo.DateUtil;
Expand Down Expand Up @@ -37,6 +39,7 @@ public class ChallengeController {
private final AnswerService answerService;
private final FamilyService familyService;
private final AnswerRepository answerRepository;
private final AwsS3Service awsS3Service;

@PostMapping // 챌린지 생성 -> 캐릭터 생성 및 답변 생성
public ResponseEntity<ApiResponse> saveChallenge(@RequestBody SaveChallengeRequest request) throws Exception {
Expand All @@ -57,11 +60,10 @@ public ResponseEntity<ApiResponse> getChallenge(@RequestParam("challengeDate") L
if (challenge == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiResponse("404", "Challenge not found", null));

}
Long currentNum = challengeService.findCurrentNum(challengeDate);
DateUtil dateUtil = DateUtil.getInstance();
ChallengeDto challengeDto = new ChallengeDto(challenge,currentNum,dateUtil.timestampToString(challengeDate));
ChallengeDto challengeDto = new ChallengeDto(challenge, currentNum, dateUtil.timestampToString(challengeDate));
return ResponseEntity.status(HttpStatus.OK)
.body(new ApiResponse("200", "Success", challengeDto));
}
Expand Down Expand Up @@ -162,7 +164,7 @@ public ResponseEntity<ApiResponse<Map<String, String>>> savePhotoAnswer(
}

// S3에 파일 업로드
String uploadImageUrl = AwsS3Service.uploadFile(answerFile);
String uploadImageUrl = awsS3Service.uploadFile(answerFile);

// Answer 객체 수정 및 저장
Optional<Answer> optionalAnswer = answerService.findAnswerByChallengeAndMember(challenge, member);
Expand Down Expand Up @@ -197,7 +199,6 @@ public ResponseEntity<ApiResponse<ChallengeDto>> getFaceChallenge(

// 챌린지 번호를 가져옴 (예를 들어, 해당 챌린지의 현재 진행 번호)
Long currentNum = challengeService.findCurrentNum(challengeId);

// 챌린지와 멤버를 통해 timestamp를 가져옴
Long timestamp = answerService.findDateByChallengeMember(challenge);

Expand All @@ -207,6 +208,85 @@ public ResponseEntity<ApiResponse<ChallengeDto>> getFaceChallenge(
return ResponseEntity.ok(new ApiResponse<>("200", "Success", challengeDto));
}

// 화상 통화 챌린지 결과 저장
@PostMapping("/face")
public ResponseEntity<ApiResponse<String>> saveFaceTimeAnswer(
@RequestParam("challengeId") Long challengeId,
@RequestParam("familyPhotos") MultipartFile[] familyPhotos) throws Exception {

if (familyPhotos.length != 4) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ApiResponse<>("400", "Exactly 4 photos must be uploaded", null));
}

Challenge challenge = challengeService.findChallengeById(challengeId);
if (challenge == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiResponse<>("404", "Challenge not found for the provided challengeId", null));
}

Member member = memberService.findMemberByLoginId();
Optional<Answer> existingAnswer = answerService.findAnswerByChallengeAndMember(challenge, member);

if (existingAnswer.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiResponse<>("404", "Answer not found for the provided challengeId and member", null));
}

// S3에 파일 업로드 및 URL 저장
List<String> uploadedUrls = new ArrayList<>();
for (MultipartFile photo : familyPhotos) {
String uploadedUrl = awsS3Service.uploadFile(photo);
uploadedUrls.add(uploadedUrl);
}

// JSON 형식으로 변환
ObjectMapper objectMapper = new ObjectMapper();
String jsonAnswerContent = objectMapper.writeValueAsString(uploadedUrls);

// Answer 업데이트
Answer answer = existingAnswer.get();
answer.setAnswerContent(jsonAnswerContent);
answer.setModifiedDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss")));
answerService.saveAnswer(answer);

return ResponseEntity.ok(new ApiResponse<>("200", "Success", null));
}

// 화상 통화 챌린지 결과 조회
@GetMapping("/face/result")
public ResponseEntity<ApiResponse<Map<String, Object>>> getFaceTimeAnswer(
@RequestParam("challengeId") Long challengeId) throws Exception {

Challenge challenge = challengeService.findChallengeById(challengeId);
if (challenge == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiResponse<>("404", "Challenge not found for the provided challengeId", null));
}

Member member = memberService.findMemberByLoginId();
Optional<Answer> existingAnswer = answerService.findAnswerByChallengeAndMember(challenge, member);

if (existingAnswer.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiResponse<>("404", "Answer not found for the provided challengeId and member", null));
}

Answer answer = existingAnswer.get();
String answerContent = answer.getAnswerContent();

// JSON 형식으로 변환된 URL 목록을 Map 형태로 변환
ObjectMapper objectMapper = new ObjectMapper();
List<String> familyPhotos = objectMapper.readValue(answerContent, new TypeReference<List<String>>() {});

// 응답 데이터 생성
Map<String, Object> responseData = new HashMap<>();
responseData.put("challengeTitle", challenge.getChallengeTitle());
responseData.put("familyPhotos", familyPhotos);

return ResponseEntity.ok(new ApiResponse<>("200", "Success", responseData));
}

@Data
static class SaveChallengeRequest {
private Long challengeDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ public class FileController {

@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile multipartFile) throws IOException {
return AwsS3Service.uploadFile(multipartFile);
return awsS3Service.uploadFile(multipartFile);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
public class MemberController {

private final MemberService memberService;
private final AwsS3Service awsS3Service;

// 회원 가입
@PostMapping("/signup")
Expand Down Expand Up @@ -59,7 +60,7 @@ public ApiResponse<MemberInfoDto> updateBasicInfo(@RequestPart("userInfo") Membe

// 파일을 전송했을 경우에만 S3 파일 업로드 수행
if (memberUpdateDto.userImg().isPresent() && !memberUpdateDto.userImg().get().isEmpty()) {
uploadImageUrl = AwsS3Service.uploadFile(memberUpdateDto.userImg().get());
uploadImageUrl = awsS3Service.uploadFile(memberUpdateDto.userImg().get());
}

memberService.update(memberUpdateDto);
Expand Down
26 changes: 15 additions & 11 deletions src/main/java/ongjong/namanmoo/service/AwsS3Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.amazonaws.services.s3.AmazonS3;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -23,12 +24,13 @@
@Service
public class AwsS3Service {

private static AmazonS3 amazonS3Client;
@Autowired
private AmazonS3 amazonS3Client;

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

public static String uploadFile(MultipartFile multipartFile) throws IOException {
public String uploadFile(MultipartFile multipartFile) throws IOException {
File uploadFile = convertFile(multipartFile)
.orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File convert fail"));

Expand All @@ -39,7 +41,7 @@ public static String uploadFile(MultipartFile multipartFile) throws IOException
return uploadImageUrl;
}

private static Optional<File> convertFile(MultipartFile file) throws IOException {
private Optional<File> convertFile(MultipartFile file) throws IOException {
String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
File convertFile = new File(fileName);

Expand All @@ -53,11 +55,11 @@ private static Optional<File> convertFile(MultipartFile file) throws IOException
return Optional.empty();
}

private static String generateFileName(File uploadFile) {
private String generateFileName(File uploadFile) {
return UUID.randomUUID() + "_" + uploadFile.getName();
}

private static String uploadFileToS3(File uploadFile, String fileName) {
private String uploadFileToS3(File uploadFile, String fileName) {
amazonS3Client.putObject(
new PutObjectRequest(bucket, fileName, uploadFile)
.withCannedAcl(CannedAccessControlList.PublicRead)
Expand All @@ -68,18 +70,20 @@ private static String uploadFileToS3(File uploadFile, String fileName) {
return getS3FileURL(fileName);
}

private static String getS3FileURL(String fileName) {
private String getS3FileURL(String fileName) {
String defaultUrl = "https://s3.amazonaws.com/";
return defaultUrl + bucket + "/" + fileName;
}

private static void removeNewFile(File targetFile) {
if (!targetFile.delete()) {
log.error("File delete fail: " + targetFile.getName() + " at path: " + targetFile.getAbsolutePath());
private void removeNewFile(File targetFile) {
if (targetFile.delete()) {
log.info("File delete success");
} else {
log.info("File delete fail");
}
}

public static void delete(String fileName) {
public void delete(String fileName) {
log.info("File Delete : " + fileName);
amazonS3Client.deleteObject(bucket, fileName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class MemberServiceImpl implements MemberService {

private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
private final AwsS3Service awsS3Service;

// 회원 가입 진행
@Override
Expand Down Expand Up @@ -59,7 +60,7 @@ public void update (MemberUpdateDto memberUpdateDto) throws Exception {
// 파일을 전송했을 경우에만 S3 파일 업로드 수행
memberUpdateDto.userImg().ifPresent(image -> {
try {
String imagePath = AwsS3Service.uploadFile(image);
String imagePath = awsS3Service.uploadFile(image);
member.setMemberImage(imagePath);
} catch (IOException e) {
throw new RuntimeException("S3 업로드 중 에러가 발생했습니다.", e);
Expand Down
19 changes: 9 additions & 10 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ spring:
password: ${SPRING_DATASOURCE_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver

servlet:
multipart:
enabled: true # 멀티파트 업로드 지원 여부 (default: true)
file-size-threshold: 0B # 파일을 디스크에 저장하지 않고 메모리에 저장하는 최소 크기 (default: 0B)
location: /users/charming/temp # 업로드된 파일이 임시로 저장되는 디스크 위치 (default: WAS가 결정)
max-file-size: 20MB # 한 개 파일의 최대 사이즈 (default: 1MB)
max-request-size: 20MB # 한 개 요청의 최대 사이즈 (default: 10MB)

profiles:
include: jwt # jwt.yml 불러오기

Expand Down Expand Up @@ -33,13 +41,4 @@ cloud:
region:
static: ap-northeast-2
stack:
auto: false

servlet:
servlet:
multipart:
enabled: true # 멀티파트 업로드 지원 여부 (default: true)
file-size-threshold: 0B # 파일을 디스크에 저장하지 않고 메모리에 저장하는 최소 크기 (default: 0B)
location: /users/charming/temp # 업로드된 파일이 임시로 저장되는 디스크 위치 (default: WAS가 결정)
max-file-size: 20MB # 한 개 파일의 최대 사이즈 (default: 1MB)
max-request-size: 20MB # 한 개 요청의 최대 사이즈 (default: 10MB)
auto: false

0 comments on commit 9318e71

Please sign in to comment.