Skip to content

Commit

Permalink
Merge pull request #477 from Kernel360/develop
Browse files Browse the repository at this point in the history
2024-11-27 test
  • Loading branch information
km2535 authored Nov 27, 2024
2 parents 99544ad + f2e943c commit 6468792
Show file tree
Hide file tree
Showing 37 changed files with 335 additions and 109 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release-batch-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ jobs:
touch ./.env
echo "${{ secrets.ENV }}" > ./.env
- name: Generate logback.xml for API
- name: Generate logback.xml for BATCH
run: |
cd ./batch/src/main/resources
cat <<EOF > logback.xml
${{ secrets.LOGBACK_API }}
${{ secrets.LOGBACK_BATCH }}
EOF
- name: Set Gradle Wrapper Permissions
Expand Down
4 changes: 3 additions & 1 deletion api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ dependencies {
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.6.0'
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.5.RELEASE'

implementation "com.sksamuel.scrimage:scrimage-core:4.0.32"
implementation "com.sksamuel.scrimage:scrimage-webp:4.0.32"

implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor "org.mapstruct:mapstruct-processor:1.4.2.Final"
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '3.3.4'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import org.badminton.domain.domain.club.info.ClubUpdateInfo;
import org.badminton.domain.domain.clubmember.service.ClubMemberService;
import org.badminton.domain.domain.member.entity.Member;
import org.badminton.domain.domain.statistics.ClubStatisticsService;
import org.badminton.domain.domain.statistics.event.CreateClubEvent;
import org.badminton.domain.domain.statistics.event.ReadClubEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Expand All @@ -31,7 +33,7 @@
public class ClubFacade {
private final ClubService clubService;
private final ClubMemberService clubMemberService;
private final ClubStatisticsService clubStatisticsService;
private final ApplicationEventPublisher eventPublisher;

@Transactional(readOnly = true)
public Page<ClubCardInfo> readAllClubs(int page, int size, String sort) {
Expand All @@ -43,9 +45,8 @@ public Page<ClubCardInfo> readAllClubs(int page, int size, String sort) {
public ClubDetailsInfo readClub(String clubToken) {
var club = clubService.readClub(clubToken);
Map<Member.MemberTier, Long> memberCountByTier = club.getClubMemberCountByTier();

int clubMembersCount = clubMemberService.countExistingClub(clubToken);
clubStatisticsService.increaseVisitedClubCount(clubToken);
eventPublisher.publishEvent(new ReadClubEvent(clubToken));
return ClubDetailsInfo.from(club, memberCountByTier,
clubMembersCount);
}
Expand All @@ -58,9 +59,9 @@ public Page<ClubCardInfo> searchClubs(String keyword, int page, int size, String

@Transactional
public ClubCreateInfo createClub(ClubCreateCommand createCommand, String memberToken) {
var clubCreateInfo = clubService.createClub(createCommand);
ClubCreateInfo clubCreateInfo = clubService.createClub(createCommand);
clubMemberService.clubMemberOwner(memberToken, clubCreateInfo);
clubStatisticsService.createStatistic(clubCreateInfo);
eventPublisher.publishEvent(new CreateClubEvent(clubCreateInfo));
return clubCreateInfo;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.badminton.domain.domain.clubmember.service.ClubMemberService;
import org.badminton.domain.domain.mail.MailService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -31,9 +32,11 @@ public class ClubMemberFacade {
private final ClubApplyService clubApplyService;
private final MailService mailService;

@Transactional
public ApplyClubInfo applyClub(String memberToken, String clubToken, ClubApplyCommand command) {
ApplyClubInfo applyClubInfo = clubApplyService.applyClub(memberToken, clubToken, command.applyReason());
mailService.prepareClubApplyEmail(clubToken, memberToken);
return clubApplyService.applyClub(memberToken, clubToken, command.applyReason());
return applyClubInfo;

}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
package org.badminton.api.application.league;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.badminton.domain.domain.clubmember.service.ClubMemberService;
import org.badminton.domain.domain.league.LeagueParticipantService;
import org.badminton.domain.domain.league.info.LeagueParticipantCancelInfo;
import org.badminton.domain.domain.league.info.LeagueParticipantInfo;
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class LeagueParticipationFacade {
private final LeagueParticipantService leagueParticipantService;
private final ClubMemberService clubMemberService;
private final LeagueParticipantService leagueParticipantService;
private final ClubMemberService clubMemberService;

public LeagueParticipantInfo participateInLeague(String memberToken, String clubToken, Long leagueId) {
return leagueParticipantService.participantInLeague(memberToken, clubToken, leagueId);
}
public LeagueParticipantInfo participateInLeague(String memberToken, String clubToken, Long leagueId) {
return leagueParticipantService.participantInLeague(memberToken, clubToken, leagueId);
}

public LeagueParticipantCancelInfo cancelParticipateInLeague(String clubToken, String memberToken, Long leagueId) {
return leagueParticipantService.participantLeagueCancel(memberToken, clubToken, leagueId);
}
public LeagueParticipantCancelInfo cancelParticipateInLeague(String clubToken, String memberToken, Long leagueId) {
return leagueParticipantService.cancelLeagueParticipation(memberToken, clubToken, leagueId);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.badminton.api.aws.s3.service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Objects;

import org.badminton.api.aws.s3.model.dto.ImageUploadRequest;
import org.badminton.api.common.exception.EmptyFileException;
import org.badminton.api.common.exception.FileSizeOverException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -17,34 +19,61 @@

@RequiredArgsConstructor
public abstract class AbstractFileUploadService implements ImageService {
private static final long MAX_FILE_SIZE = 2548576; // 1.5MB
private static final String WEBP = "webp";
private static final String AVIF = "avif";
private static final String CONTENT_TYPE = "image/webp";

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

private final AmazonS3 s3Client;
private final ImageConversionService imageConversionService;
private static final String S3_URL_PREFIX = "https://badminton-team.s3.ap-northeast-2.amazonaws.com";
private static final String CLOUDFRONT_URL_PREFIX = "https://d36om9pjoifd2y.cloudfront.net";

public String uploadFile(ImageUploadRequest file) {
MultipartFile uploadFile = file.multipartFile();
if (uploadFile.getSize() > MAX_FILE_SIZE) {
throw new FileSizeOverException(uploadFile.getSize());
}

// 파일이 비어있거나 파일 이름이 없는 경우 체크
if (uploadFile.isEmpty() || Objects.isNull(uploadFile.getOriginalFilename())) {
throw new EmptyFileException();
}
String fileName = makeFileName(uploadFile.getOriginalFilename());

ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(uploadFile.getSize());
objectMetadata.setContentType(uploadFile.getContentType());
try {
String fileExtension = getFileExtension(uploadFile.getOriginalFilename());

byte[] processedImage = processImage(uploadFile, fileExtension);
String newFileExtension = determineNewFileExtension(fileExtension);
String fileName = makeFileName(newFileExtension);

ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(processedImage.length);
objectMetadata.setContentType(CONTENT_TYPE);

// S3에 파일 업로드
s3Client.putObject(new PutObjectRequest(bucket, fileName,
uploadFile.getInputStream(), objectMetadata)
new ByteArrayInputStream(processedImage), objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));

// 업로드 후 CloudFront URL 반환
return toCloudFrontUrl(s3Client.getUrl(bucket, fileName).toString());
} catch (IOException exception) {
throw new EmptyFileException(exception);

} catch (IOException e) {
throw new EmptyFileException();
}
}

private String getFileExtension(String filename) {
int dotIndex = filename.lastIndexOf(".");
if (dotIndex == -1) {
return "";
}
return filename.substring(dotIndex + 1);
}

private String toCloudFrontUrl(String originUrl) {
if (originUrl != null && originUrl.startsWith(S3_URL_PREFIX)) {
return originUrl.replace(S3_URL_PREFIX, CLOUDFRONT_URL_PREFIX);
Expand All @@ -53,8 +82,21 @@ private String toCloudFrontUrl(String originUrl) {
}
}

@Override
public abstract String makeFileName(String originalFilename);
private byte[] processImage(MultipartFile file, String extension) throws IOException {
if (WEBP.equalsIgnoreCase(extension) || AVIF.equalsIgnoreCase(extension)) {
return file.getBytes();
}
return imageConversionService.convertToWebP(file);
}

}
private String determineNewFileExtension(String extension) {
if (WEBP.equalsIgnoreCase(extension) || AVIF.equalsIgnoreCase(extension)) {
return extension;
} else {
return WEBP;
}
}

@Override
public abstract String makeFileName(String newFileExtension);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
@Service
public class ClubImageService extends AbstractFileUploadService {

public ClubImageService(AmazonS3 s3Client) {
super(s3Client);
public ClubImageService(AmazonS3 s3Client, ImageConversionService imageConversionService) {
super(s3Client, imageConversionService);
}

@Override
Expand All @@ -20,10 +20,8 @@ public String uploadFile(ImageUploadRequest file) {
}

@Override
public String makeFileName(String originalFilename) {
String[] originFile = originalFilename.split("\\.");
String extension = originFile[originFile.length - 1];
return "club-banner/" + UUID.randomUUID() + "." + extension;
public String makeFileName(String newFileExtension) {
return "club-banner/" + UUID.randomUUID() + "." + newFileExtension;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.badminton.api.aws.s3.service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.badminton.api.common.exception.EmptyFileException;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.sksamuel.scrimage.ImmutableImage;
import com.sksamuel.scrimage.metadata.ImageMetadata;
import com.sksamuel.scrimage.webp.WebpWriter;

@Service
public class ImageConversionService {

public byte[] convertToWebP(MultipartFile file) {
try {
ImmutableImage image = ImmutableImage.loader().fromStream(file.getInputStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
WebpWriter writer = WebpWriter.DEFAULT;
writer.write(image, ImageMetadata.fromStream(file.getInputStream()), outputStream);
return outputStream.toByteArray();
} catch (IOException exception) {
throw new EmptyFileException(exception);
}

}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@

package org.badminton.api.aws.s3.service;

import org.badminton.api.aws.s3.model.dto.ImageUploadRequest;

public interface ImageService {
String uploadFile(ImageUploadRequest file);

String makeFileName(String originalFilename);
String makeFileName(String newFileExtension);
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.UUID;

import org.badminton.api.aws.s3.model.dto.ImageUploadRequest;
import org.badminton.api.common.exception.member.MemberNotExistException;
import org.badminton.domain.domain.member.service.MemberService;
import org.springframework.stereotype.Service;

Expand All @@ -11,25 +12,24 @@
@Service
public class MemberProfileImageService extends AbstractFileUploadService {

private String currentMemberToken;
private String currentMemberToken;

public MemberProfileImageService(AmazonS3 s3Client, MemberService memberService) {
super(s3Client);
}
public MemberProfileImageService(AmazonS3 s3Client, MemberService memberService,
ImageConversionService imageConversionService) {
super(s3Client, imageConversionService);
}

public String uploadFile(ImageUploadRequest file, String memberToken) {
this.currentMemberToken = memberToken;
return super.uploadFile(file);
public String uploadFile(ImageUploadRequest file, String memberToken) {
this.currentMemberToken = memberToken;
return super.uploadFile(file);

}
}

@Override
public String makeFileName(String originalFilename) {
if (this.currentMemberToken == null) {
throw new IllegalStateException("Member ID is not set. Make sure to call uploadFile method first.");
}
String[] originFile = originalFilename.split("\\.");
String extension = originFile[originFile.length - 1];
return "member-profile/" + UUID.randomUUID() + "." + extension;
}
@Override
public String makeFileName(String originalFilename) {
if (this.currentMemberToken == null) {
throw new MemberNotExistException("멤버 토큰이 없습니다. 로그인을 먼저 해주세요.");
}
return "member-profile/" + UUID.randomUUID() + ".webp";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import org.badminton.domain.common.exception.BadmintonException;

public class EmptyFileException extends BadmintonException {
public EmptyFileException() {
super(ErrorCode.FILE_NOT_EXIST);
}
public EmptyFileException() {
super(ErrorCode.FILE_NOT_EXIST);
}

public EmptyFileException(Exception exception) {
super(ErrorCode.FILE_NOT_EXIST, exception);
}
public EmptyFileException(Exception exception) {
super(ErrorCode.FILE_NOT_EXIST, exception);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.badminton.api.common.exception;

import org.badminton.domain.common.error.ErrorCode;
import org.badminton.domain.common.exception.BadmintonException;

public class FileSizeOverException extends BadmintonException {
public FileSizeOverException(long fileSize) {
super(ErrorCode.FILE_SIZE_OVER, "[입력한 파일 사이즈 : " + fileSize + ", 최대 파일 사이즈 : 2.5MB]");
}

public FileSizeOverException(Exception exception) {
super(ErrorCode.FILE_SIZE_OVER, exception);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ public SecurityFilterChain clubFilterChain(HttpSecurity http) throws Exception {
.requestMatchers(HttpMethod.POST, "/v1/clubs/images")
.access(hasClubRole("OWNER", "MANAGER"))
.requestMatchers(HttpMethod.DELETE, "/v1/clubs/{clubToken}/leagues/{leagueId}")
.access(hasClubRole("OWNER", "MANAGER"))
.access(hasClubRole("OWNER", "MANAGER", "USER"))
.requestMatchers(HttpMethod.PATCH, "/v1/clubs/{clubToken}/leagues/{leagueId}")
.access(hasClubRole("OWNER", "MANAGER"))
.access(hasClubRole("OWNER", "MANAGER", "USER"))
.requestMatchers(HttpMethod.POST, "/v1/clubs/{clubToken}/leagues/{leagueId}/participation",
"/v1/clubs/{clubToken}/leagues")
.access(hasClubRole("OWNER", "MANAGER", "USER"))
Expand Down
Loading

0 comments on commit 6468792

Please sign in to comment.