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
20 changes: 18 additions & 2 deletions src/main/java/com/divary/domain/image/service/ImageService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -237,10 +238,25 @@ public ImageResponse uploadImage(ImageUploadRequest request) {
}

// 경로 패턴으로 이미지 목록 조회
@Transactional(readOnly = true)
public List<ImageResponse> getImagesByPath(String pathPattern) {
List<Image> images = imageRepository.findByS3KeyStartingWith(pathPattern);

return images.stream()
.map(image -> ImageResponse.from(image, imageStorageService.generatePublicUrl(image.getS3Key())))
.map(image -> {
String s3Key = image.getS3Key();
String fileUrl;

//s3Key에 license가 포함된 경우 Pre-signed URL 생성
if (s3Key != null && s3Key.contains("/license/")) {
fileUrl = imageStorageService.generatePreSignedUrl(s3Key, Duration.ofMinutes(10));
log.info("라이센스 이미지 Pre-signed URL 생성 완료: {}", fileUrl);
} else {
fileUrl = imageStorageService.generatePublicUrl(s3Key);
}//이외의 경우에는 일반 public url 생성

return ImageResponse.from(image, fileUrl);
})
.collect(Collectors.toList());
}

Expand All @@ -252,7 +268,7 @@ public ImageResponse getImageById(Long imageId) {
String fileUrl = imageStorageService.generatePublicUrl(image.getS3Key());
return ImageResponse.from(image, fileUrl);
}

// 이미지 삭제
@Transactional
public void deleteImage(Long imageId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;

import java.io.IOException;
import java.time.Duration;
import java.util.List;
import java.util.UUID;

Expand Down Expand Up @@ -128,6 +132,32 @@ public String generatePublicUrl(String s3Key) {
return String.format("https://%s.s3.%s.amazonaws.com/%s", bucketName, region, s3Key);
}

public String generatePreSignedUrl(String s3Key, Duration expiry) {
try (S3Presigner presigner = S3Presigner.builder()
.region(Region.of(region))
.credentialsProvider(DefaultCredentialsProvider.create())
.build()) {

GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(bucketName)
.key(s3Key)
.build();

GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
.signatureDuration(expiry)
.getObjectRequest(getObjectRequest)
.build();

PresignedGetObjectRequest presigned = presigner.presignGetObject(presignRequest);
return presigned.url().toString();

} catch (Exception e) {
log.error("Pre-signed URL 생성 실패: {}", e.getMessage());
throw new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR);
}
}



// S3 URL에서 S3 키 추출
public String extractS3KeyFromUrl(String imageUrl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
import java.util.Optional;

import com.mysql.cj.log.Log;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface LogBaseInfoRepository extends JpaRepository<LogBaseInfo, Long> {
@Query("SELECT l FROM LogBaseInfo l WHERE YEAR(l.date) = :year AND l.saveStatus = :status AND l.member = :member ORDER BY l.date DESC")
List<LogBaseInfo> findByYearAndStatusAndMember(@Param("year") int year, @Param("status") SaveStatus status, @Param("member") Member member);
@Query("SELECT l FROM LogBaseInfo l WHERE YEAR(l.date) = :year AND l.saveStatus = :status AND l.member = :member")
List<LogBaseInfo> findByYearAndStatusAndMember(@Param("year") int year, @Param("status") SaveStatus status, @Param("member") Member member, Sort sort);

@Query("SELECT l FROM LogBaseInfo l WHERE YEAR(l.date) = :year AND l.member = :member ORDER BY l.date DESC")
List<LogBaseInfo> findByYearAndMember(@Param("year") int year, @Param("member") Member member);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
Expand Down Expand Up @@ -54,11 +55,21 @@ public class LogBookController {
public ApiResponse<List<LogBaseListResultDTO>> getLogListByYearAndStatus(
@RequestParam int year,
@RequestParam(required = false) SaveStatus saveStatus,
@AuthenticationPrincipal CustomUserPrincipal userPrincipal) {
@AuthenticationPrincipal CustomUserPrincipal userPrincipal,
@RequestParam(required = false) String sort)
{
Sort sortOption;

if ("oldest".equalsIgnoreCase(sort)) {
sortOption = Sort.by(Sort.Direction.ASC, "date");
}
else {
sortOption = Sort.by(Sort.Direction.DESC, "date");
}

Long userId = userPrincipal.getId();

List<LogBaseListResultDTO> result = logBookService.getLogBooksByYearAndStatus(year, saveStatus, userId);
List<LogBaseListResultDTO> result = logBookService.getLogBooksByYearAndStatus(year, saveStatus, userId, sortOption);
return ApiResponse.success(result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.divary.global.exception.ErrorCode;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -59,7 +60,7 @@ public class LogBookService {
}

@Transactional(readOnly = true)
public List<LogBaseListResultDTO> getLogBooksByYearAndStatus(int year, SaveStatus status, Long userId) {
public List<LogBaseListResultDTO> getLogBooksByYearAndStatus(int year, SaveStatus status, Long userId, Sort sort) {

List<LogBaseInfo> logBaseInfoList;

Expand All @@ -70,7 +71,7 @@ public List<LogBaseListResultDTO> getLogBooksByYearAndStatus(int year, SaveStatu
// 쿼리스트링 없을 경우 전체 조회
logBaseInfoList = logBaseInfoRepository.findByYearAndMember(year,member);
} else {
logBaseInfoList = logBaseInfoRepository.findByYearAndStatusAndMember(year,status,member);
logBaseInfoList = logBaseInfoRepository.findByYearAndStatusAndMember(year,status,member,sort);
}

return logBaseInfoList.stream()
Expand Down Expand Up @@ -131,11 +132,6 @@ public LogDetailCreateResultDTO createLogDetail(Long logBaseInfoId, Long userId)
LogBaseInfo base = logBaseInfoRepository.findByIdAndMemberId(logBaseInfoId,userId)
.orElseThrow(() -> new BusinessException(ErrorCode.LOG_BASE_NOT_FOUND));

// 연결된 기존의 로그북 개수 확인
if (logBookRepository.countByLogBaseInfo(base) >= 3) {
throw new BusinessException(ErrorCode.LOG_LIMIT_EXCEEDED);
}//하루 최대 3개 넘으면 에러 던지기

LogBook logBook = LogBook.builder()
.logBaseInfo(base)
.build();
Expand Down Expand Up @@ -170,17 +166,12 @@ public void calculateLogBaseStatus(LogBaseInfo base) {
}
return; // 베이스를 TEMP로 맞췄으니 종료
}

// 2. 연결된 로그북들 모두 COMPLETE인지 확인
long total = logBookRepository.countByLogBaseInfoId(base.getId());
if (total > 0) {
long completeCount = logBookRepository.countByLogBaseInfoIdAndSaveStatus(base.getId(), SaveStatus.COMPLETE);
if (completeCount == total) {
if (base.getSaveStatus() != SaveStatus.COMPLETE) {
base.setSaveStatus(SaveStatus.COMPLETE);
}
else {
if (base.getSaveStatus() != SaveStatus.COMPLETE) {
base.setSaveStatus(SaveStatus.COMPLETE);
}
}

}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.divary.domain.member.controller;

import com.divary.common.response.ApiResponse;
import com.divary.domain.member.dto.requestDTO.MyPageGroupRequestDTO;
import com.divary.domain.member.dto.requestDTO.MyPageLevelRequestDTO;
import com.divary.domain.member.dto.response.MyPageImageResponseDTO;
import com.divary.domain.member.dto.response.MyPageProfileResponseDTO;
import com.divary.global.config.SwaggerConfig;
import com.divary.global.config.security.CustomUserPrincipal;
import com.divary.global.exception.ErrorCode;
Expand Down Expand Up @@ -39,4 +41,27 @@ public ApiResponse licenseUpload(@AuthenticationPrincipal CustomUserPrincipal us
return ApiResponse.success(response);
}

@PatchMapping("/group")
@SwaggerConfig.ApiSuccessResponse(dataType = Void.class)
@SwaggerConfig.ApiErrorExamples(value = {ErrorCode.INVALID_INPUT_VALUE, ErrorCode.AUTHENTICATION_REQUIRED})
public ApiResponse updateGroup(@AuthenticationPrincipal CustomUserPrincipal userPrincipal, @Valid @RequestBody MyPageGroupRequestDTO requestDTO) {
memberService.updateGroup(userPrincipal.getId(), requestDTO);
return ApiResponse.success(null);
}

@GetMapping("/profile")
@SwaggerConfig.ApiSuccessResponse(dataType = Void.class)
@SwaggerConfig.ApiErrorExamples(value = {ErrorCode.AUTHENTICATION_REQUIRED})
public ApiResponse getProfile(@AuthenticationPrincipal CustomUserPrincipal userPrincipal){
MyPageProfileResponseDTO responseDTO = memberService.getMemberProfile(userPrincipal.getId());
return ApiResponse.success(responseDTO);
}

@GetMapping("/license")
@SwaggerConfig.ApiSuccessResponse(dataType = Void.class)
@SwaggerConfig.ApiErrorExamples(value = {ErrorCode.AUTHENTICATION_REQUIRED})
public ApiResponse getLicense(@AuthenticationPrincipal CustomUserPrincipal userPrincipal){
MyPageImageResponseDTO responseDTO = memberService.getLicenseImage(userPrincipal.getId());
return ApiResponse.success(responseDTO);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.divary.domain.member.dto.requestDTO;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
public class MyPageGroupRequestDTO {

@Schema(description = "Group", example = "PADI")
private String memberGroup;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.divary.domain.member.dto.response;

import com.divary.domain.member.enums.Levels;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class MyPageProfileResponseDTO {

@Schema(description = "멤버 id", example = "user1234")
private String id;

@Schema(description = "단체명", example = "PADI")
private String memberGroup;

@Schema(description = "레벨", example = "오픈워터 다이버")
private Levels level;

}
8 changes: 8 additions & 0 deletions src/main/java/com/divary/domain/member/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public class Member extends BaseEntity {
@NotNull
Status status = Status.ACTIVE; // 사용자 상태

@Column
private String memberGroup;

private LocalDateTime deactivatedAt; //비활성화 된 시간과 날짜

@Version
Expand All @@ -54,4 +57,9 @@ public void cancelDeletion() {
this.status = Status.ACTIVE;
this.deactivatedAt = null;
}

public void updateGroup(String newGroup){
this.memberGroup = newGroup;
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.divary.domain.member.service;

import com.divary.domain.member.dto.requestDTO.MyPageGroupRequestDTO;
import com.divary.domain.member.dto.response.MyPageImageResponseDTO;
import com.divary.domain.member.dto.response.MyPageProfileResponseDTO;
import com.divary.domain.member.entity.Member;
import com.divary.domain.member.dto.requestDTO.MyPageLevelRequestDTO;
import com.divary.global.oauth.dto.response.DeactivateResponse;
Expand All @@ -16,5 +18,9 @@ public interface MemberService {
void cancelDeleteMember(Long memberId);
public Member findOrCreateMember(String email);

void updateGroup(Long userId, MyPageGroupRequestDTO requestDTO);

MyPageProfileResponseDTO getMemberProfile(Long userId);

MyPageImageResponseDTO getLicenseImage(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import com.divary.common.util.EnumValidator;
import com.divary.domain.image.dto.request.ImageUploadRequest;
import com.divary.domain.image.dto.response.ImageResponse;
import com.divary.domain.image.service.ImageService;
import com.divary.domain.member.dto.requestDTO.MyPageGroupRequestDTO;
import com.divary.domain.member.dto.requestDTO.MyPageLevelRequestDTO;
import com.divary.domain.member.dto.response.MyPageImageResponseDTO;
import com.divary.domain.member.dto.response.MyPageProfileResponseDTO;
import com.divary.domain.member.enums.Role;
import com.divary.domain.member.enums.Status;
import com.divary.global.exception.BusinessException;
Expand All @@ -25,6 +28,7 @@
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
Expand Down Expand Up @@ -145,4 +149,40 @@ public Member findOrCreateMember(String email) {
return memberRepository.save(newMember);
});
}
@Override
@CacheEvict(cacheNames = com.divary.global.config.CacheConfig.CACHE_MEMBER_BY_ID, key = "#userId")
public void updateGroup(Long userId, MyPageGroupRequestDTO requestDTO){
String group = requestDTO.getMemberGroup();

Member member = memberRepository.findById(userId).orElseThrow(()-> new BusinessException(ErrorCode.MEMBER_NOT_FOUND));
member.updateGroup(group);
}

@Override
public MyPageProfileResponseDTO getMemberProfile(Long userId){
Member member = memberRepository.findById(userId).orElseThrow(()->new BusinessException(ErrorCode.MEMBER_NOT_FOUND));

String memberIdByEmail = member.getEmail().split("@")[0];
// 프로필에 나오는 아이디: 이메일에서 @ 앞부분만 추출

return MyPageProfileResponseDTO.builder()
.memberGroup(member.getMemberGroup())
.level(member.getLevel())
.id(memberIdByEmail)
.build();
}

@Override
public MyPageImageResponseDTO getLicenseImage(Long userId){

String uploadPath = "users/" + userId + "/license/";

List<ImageResponse> imageResponses = imageService.getImagesByPath(uploadPath);

String fileUrl = imageResponses.getFirst().getFileUrl();

return new MyPageImageResponseDTO(fileUrl);
}


}